1 /* Code and data for the IBM console driver.
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.
19 #include "../drivers.h"
21 #include <sys/ioctl.h>
23 #include <minix/callnr.h>
24 #include <minix/com.h>
27 #include "../../kernel/const.h"
28 #include "../../kernel/config.h"
29 #include "../../kernel/type.h"
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 */
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 */
53 /* The clock task should provide an interface for this */
54 #define TIMER_FREQ 1193182L /* clock frequency for timer in PC and AT */
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 */
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
68 /* Global variables used by the console driver and assembly support. */
69 PUBLIC
int vid_index
; /* index of video segment in remote mem map */
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 */
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 */
86 PRIVATE
int disabled_vc
= -1; /* Virtual console that was active when
87 * disable_console was called.
89 PRIVATE
int disabled_sm
; /* Scroll mode to be restored when re-enabling
93 /* Per console data. */
94 typedef struct console
{
95 tty_t
*c_tty
; /* associated TTY struct */
96 int c_column
; /* current column number (0-origin) */
97 int c_row
; /* current row (0 at top of screen) */
98 int c_rwords
; /* number of WORDS (not bytes) in outqueue */
99 unsigned c_start
; /* start of video memory of this console */
100 unsigned c_limit
; /* limit of this console's video memory */
101 unsigned c_org
; /* location in RAM where 6845 base points */
102 unsigned c_cur
; /* current position of cursor in video RAM */
103 unsigned c_attr
; /* character attribute */
104 unsigned c_blank
; /* blank attribute */
105 char c_reverse
; /* reverse video */
106 char c_esc_state
; /* 0=normal, 1=ESC, 2=ESC[ */
107 char c_esc_intro
; /* Distinguishing character following ESC */
108 int *c_esc_parmp
; /* pointer to current escape parameter */
109 int c_esc_parmv
[MAX_ESC_PARMS
]; /* list of escape parameters */
110 u16_t c_ramqueue
[CONS_RAM_WORDS
]; /* buffer for video RAM */
113 PRIVATE
int nr_cons
= 1; /* actual number of consoles */
114 PRIVATE console_t cons_table
[NR_CONS
];
115 PRIVATE console_t
*curcons
; /* currently visible */
117 /* Color if using a color controller. */
118 #define color (vid_port == C_6845)
120 /* Map from ANSI colors to the attributes used by the PC */
121 PRIVATE
int ansi_colors
[8] = {0, 4, 2, 6, 1, 5, 3, 7};
123 /* Structure used for font management */
125 unsigned short index
;
130 FORWARD
_PROTOTYPE( int cons_write
, (struct tty
*tp
, int try) );
131 FORWARD
_PROTOTYPE( void cons_echo
, (tty_t
*tp
, int c
) );
132 FORWARD
_PROTOTYPE( void out_char
, (console_t
*cons
, int c
) );
133 FORWARD
_PROTOTYPE( void cons_putk
, (int c
) );
134 FORWARD
_PROTOTYPE( void beep
, (void) );
135 FORWARD
_PROTOTYPE( void do_escape
, (console_t
*cons
, int c
) );
136 FORWARD
_PROTOTYPE( void flush
, (console_t
*cons
) );
137 FORWARD
_PROTOTYPE( void parse_escape
, (console_t
*cons
, int c
) );
138 FORWARD
_PROTOTYPE( void scroll_screen
, (console_t
*cons
, int dir
) );
139 FORWARD
_PROTOTYPE( void set_6845
, (int reg
, unsigned val
) );
140 FORWARD
_PROTOTYPE( void get_6845
, (int reg
, unsigned *val
) );
141 FORWARD
_PROTOTYPE( void stop_beep
, (timer_t
*tmrp
) );
142 FORWARD
_PROTOTYPE( void cons_org0
, (void) );
143 FORWARD
_PROTOTYPE( void disable_console
, (void) );
144 FORWARD
_PROTOTYPE( void reenable_console
, (void) );
145 FORWARD
_PROTOTYPE( int ga_program
, (struct sequence
*seq
) );
146 FORWARD
_PROTOTYPE( int cons_ioctl
, (tty_t
*tp
, int) );
147 FORWARD
_PROTOTYPE( void ser_putc
, (char c
) );
149 /*===========================================================================*
151 *===========================================================================*/
152 PRIVATE
int cons_write(tp
, try)
153 register struct tty
*tp
; /* tells which terminal is to be used */
156 /* Copy as much data as possible to the output queue, then start I/O. On
157 * memory-mapped terminals, such as the IBM console, the I/O will also be
158 * finished, and the counts updated. Keep repeating until all I/O done.
165 console_t
*cons
= tp
->tty_priv
;
167 if (try) return 1; /* we can always write to console */
169 /* Check quickly for nothing to do, so this can be called often without
170 * unmodular tests elsewhere.
172 if ((count
= tp
->tty_outleft
) == 0 || tp
->tty_inhibited
) return;
174 /* Copy the user bytes to buf[] for decent addressing. Loop over the
175 * copies, since the user buffer may be much larger than buf[].
178 if (count
> sizeof(buf
)) count
= sizeof(buf
);
179 if(tp
->tty_out_safe
) {
180 if ((result
= sys_safecopyfrom(tp
->tty_outproc
, tp
->tty_out_vir_g
,
181 tp
->tty_out_vir_offset
, (vir_bytes
) buf
, count
, D
)) != OK
)
183 tp
->tty_out_vir_offset
+= count
;
185 if ((result
= sys_vircopy(tp
->tty_outproc
, D
, tp
->tty_out_vir_g
,
186 SELF
, D
, (vir_bytes
) buf
, (vir_bytes
) count
)) != OK
)
188 tp
->tty_out_vir_g
+= count
;
192 /* Update terminal data structure. */
193 tp
->tty_outcum
+= count
;
194 tp
->tty_outleft
-= count
;
196 /* Output each byte of the copy to the screen. Avoid calling
197 * out_char() for the "easy" characters, put them into the buffer
201 if ((unsigned) *tbuf
< ' ' || cons
->c_esc_state
> 0
202 || cons
->c_column
>= scr_width
203 || cons
->c_rwords
>= buflen(cons
->c_ramqueue
))
205 out_char(cons
, *tbuf
++);
207 cons
->c_ramqueue
[cons
->c_rwords
++] =
208 cons
->c_attr
| (*tbuf
++ & BYTE
);
211 } while (--count
!= 0);
212 } while ((count
= tp
->tty_outleft
) != 0 && !tp
->tty_inhibited
);
214 flush(cons
); /* transfer anything buffered to the screen */
216 /* Reply to the writer if all output is finished or if an error occured. */
217 if (tp
->tty_outleft
== 0 || result
!= OK
) {
218 /* REVIVE is not possible. I/O on memory mapped consoles finishes. */
219 tty_reply(tp
->tty_outrepcode
, tp
->tty_outcaller
, tp
->tty_outproc
,
225 /*===========================================================================*
227 *===========================================================================*/
228 PRIVATE
void cons_echo(tp
, c
)
229 register tty_t
*tp
; /* pointer to tty struct */
230 int c
; /* character to be echoed */
232 /* Echo keyboard input (print & flush). */
233 console_t
*cons
= tp
->tty_priv
;
239 /*===========================================================================*
241 *===========================================================================*/
242 PRIVATE
void out_char(cons
, c
)
243 register console_t
*cons
; /* pointer to console struct */
244 int c
; /* character to be output */
246 /* Output a character on the console. Check for escape sequences first. */
247 if (cons
->c_esc_state
> 0) {
248 parse_escape(cons
, c
);
253 case 000: /* null is typically used for padding */
254 return; /* better not do anything */
256 case 007: /* ring the bell */
257 flush(cons
); /* print any chars queued for output */
261 case '\b': /* backspace */
262 if (--cons
->c_column
< 0) {
263 if (--cons
->c_row
>= 0) cons
->c_column
+= scr_width
;
268 case '\n': /* line feed */
269 if ((cons
->c_tty
->tty_termios
.c_oflag
& (OPOST
|ONLCR
))
274 case 013: /* CTRL-K */
275 case 014: /* CTRL-L */
276 if (cons
->c_row
== scr_lines
-1) {
277 scroll_screen(cons
, SCROLL_UP
);
284 case '\r': /* carriage return */
290 cons
->c_column
= (cons
->c_column
+ TAB_SIZE
) & ~TAB_MASK
;
291 if (cons
->c_column
> scr_width
) {
292 cons
->c_column
-= scr_width
;
293 if (cons
->c_row
== scr_lines
-1) {
294 scroll_screen(cons
, SCROLL_UP
);
302 case 033: /* ESC - start of an escape sequence */
303 flush(cons
); /* print any chars queued for output */
304 cons
->c_esc_state
= 1; /* mark ESC as seen */
307 default: /* printable chars are stored in ramqueue */
308 if (cons
->c_column
>= scr_width
) {
309 if (!LINEWRAP
) return;
310 if (cons
->c_row
== scr_lines
-1) {
311 scroll_screen(cons
, SCROLL_UP
);
318 if (cons
->c_rwords
== buflen(cons
->c_ramqueue
)) flush(cons
);
319 cons
->c_ramqueue
[cons
->c_rwords
++] = cons
->c_attr
| (c
& BYTE
);
320 cons
->c_column
++; /* next column */
325 /*===========================================================================*
327 *===========================================================================*/
328 PRIVATE
void scroll_screen(cons
, dir
)
329 register console_t
*cons
; /* pointer to console struct */
330 int dir
; /* SCROLL_UP or SCROLL_DOWN */
332 unsigned new_line
, new_org
, chars
;
335 chars
= scr_size
- scr_width
; /* one screen minus one line */
337 /* Scrolling the screen is a real nuisance due to the various incompatible
338 * video cards. This driver supports software scrolling (Hercules?),
339 * hardware scrolling (mono and CGA cards) and hardware scrolling without
340 * wrapping (EGA cards). In the latter case we must make sure that
341 * c_start <= c_org && c_org + scr_size <= c_limit
342 * holds, because EGA doesn't wrap around the end of video memory.
344 if (dir
== SCROLL_UP
) {
345 /* Scroll one line up in 3 ways: soft, avoid wrap, use origin. */
347 vid_vid_copy(cons
->c_start
+ scr_width
, cons
->c_start
, chars
);
349 if (!wrap
&& cons
->c_org
+ scr_size
+ scr_width
>= cons
->c_limit
) {
350 vid_vid_copy(cons
->c_org
+ scr_width
, cons
->c_start
, chars
);
351 cons
->c_org
= cons
->c_start
;
353 cons
->c_org
= (cons
->c_org
+ scr_width
) & vid_mask
;
355 new_line
= (cons
->c_org
+ chars
) & vid_mask
;
357 /* Scroll one line down in 3 ways: soft, avoid wrap, use origin. */
359 vid_vid_copy(cons
->c_start
, cons
->c_start
+ scr_width
, chars
);
361 if (!wrap
&& cons
->c_org
< cons
->c_start
+ scr_width
) {
362 new_org
= cons
->c_limit
- scr_size
;
363 vid_vid_copy(cons
->c_org
, new_org
+ scr_width
, chars
);
364 cons
->c_org
= new_org
;
366 cons
->c_org
= (cons
->c_org
- scr_width
) & vid_mask
;
368 new_line
= cons
->c_org
;
370 /* Blank the new line at top or bottom. */
371 blank_color
= cons
->c_blank
;
372 mem_vid_copy(BLANK_MEM
, new_line
, scr_width
);
374 /* Set the new video origin. */
375 if (cons
== curcons
) set_6845(VID_ORG
, cons
->c_org
);
379 /*===========================================================================*
381 *===========================================================================*/
382 PRIVATE
void flush(cons
)
383 register console_t
*cons
; /* pointer to console struct */
385 /* Send characters buffered in 'ramqueue' to screen memory, check the new
386 * cursor position, compute the new hardware cursor position and set it.
389 tty_t
*tp
= cons
->c_tty
;
391 /* Have the characters in 'ramqueue' transferred to the screen. */
392 if (cons
->c_rwords
> 0) {
393 mem_vid_copy(cons
->c_ramqueue
, cons
->c_cur
, cons
->c_rwords
);
396 /* TTY likes to know the current column and if echoing messed up. */
397 tp
->tty_position
= cons
->c_column
;
398 tp
->tty_reprint
= TRUE
;
401 /* Check and update the cursor position. */
402 if (cons
->c_column
< 0) cons
->c_column
= 0;
403 if (cons
->c_column
> scr_width
) cons
->c_column
= scr_width
;
404 if (cons
->c_row
< 0) cons
->c_row
= 0;
405 if (cons
->c_row
>= scr_lines
) cons
->c_row
= scr_lines
- 1;
406 cur
= cons
->c_org
+ cons
->c_row
* scr_width
+ cons
->c_column
;
407 if (cur
!= cons
->c_cur
) {
408 if (cons
== curcons
) set_6845(CURSOR
, cur
);
413 /*===========================================================================*
415 *===========================================================================*/
416 PRIVATE
void parse_escape(cons
, c
)
417 register console_t
*cons
; /* pointer to console struct */
418 char c
; /* next character in escape sequence */
420 /* The following ANSI escape sequences are currently supported.
421 * If n and/or m are omitted, they default to 1.
422 * ESC [nA moves up n lines
423 * ESC [nB moves down n lines
424 * ESC [nC moves right n spaces
425 * ESC [nD moves left n spaces
426 * ESC [m;nH" moves cursor to (m,n)
427 * ESC [J clears screen from cursor
428 * ESC [K clears line from cursor
429 * ESC [nL inserts n lines ar cursor
430 * ESC [nM deletes n lines at cursor
431 * ESC [nP deletes n chars at cursor
432 * ESC [n@ inserts n chars at cursor
433 * ESC [nm enables rendition n (0=normal, 4=bold, 5=blinking, 7=reverse)
434 * ESC M scrolls the screen backwards if the cursor is on the top line
437 switch (cons
->c_esc_state
) {
438 case 1: /* ESC seen */
439 cons
->c_esc_intro
= '\0';
440 cons
->c_esc_parmp
= bufend(cons
->c_esc_parmv
);
442 *--cons
->c_esc_parmp
= 0;
443 } while (cons
->c_esc_parmp
> cons
->c_esc_parmv
);
445 case '[': /* Control Sequence Introducer */
446 cons
->c_esc_intro
= c
;
447 cons
->c_esc_state
= 2;
449 case 'M': /* Reverse Index */
453 cons
->c_esc_state
= 0;
457 case 2: /* ESC [ seen */
458 if (c
>= '0' && c
<= '9') {
459 if (cons
->c_esc_parmp
< bufend(cons
->c_esc_parmv
))
460 *cons
->c_esc_parmp
= *cons
->c_esc_parmp
* 10 + (c
-'0');
463 if (cons
->c_esc_parmp
< bufend(cons
->c_esc_parmv
))
472 /*===========================================================================*
474 *===========================================================================*/
475 PRIVATE
void do_escape(cons
, c
)
476 register console_t
*cons
; /* pointer to console struct */
477 char c
; /* next character in escape sequence */
480 unsigned src
, dst
, count
;
483 /* Some of these things hack on screen RAM, so it had better be up to date */
486 if (cons
->c_esc_intro
== '\0') {
487 /* Handle a sequence beginning with just ESC */
489 case 'M': /* Reverse Index */
490 if (cons
->c_row
== 0) {
491 scroll_screen(cons
, SCROLL_DOWN
);
501 if (cons
->c_esc_intro
== '[') {
502 /* Handle a sequence beginning with ESC [ and parameters */
503 value
= cons
->c_esc_parmv
[0];
505 case 'A': /* ESC [nA moves up n lines */
506 n
= (value
== 0 ? 1 : value
);
511 case 'B': /* ESC [nB moves down n lines */
512 n
= (value
== 0 ? 1 : value
);
517 case 'C': /* ESC [nC moves right n spaces */
518 n
= (value
== 0 ? 1 : value
);
523 case 'D': /* ESC [nD moves left n spaces */
524 n
= (value
== 0 ? 1 : value
);
529 case 'H': /* ESC [m;nH" moves cursor to (m,n) */
530 cons
->c_row
= cons
->c_esc_parmv
[0] - 1;
531 cons
->c_column
= cons
->c_esc_parmv
[1] - 1;
535 case 'J': /* ESC [sJ clears in display */
537 case 0: /* Clear from cursor to end of screen */
538 count
= scr_size
- (cons
->c_cur
- cons
->c_org
);
541 case 1: /* Clear from start of screen to cursor */
542 count
= cons
->c_cur
- cons
->c_org
;
545 case 2: /* Clear entire screen */
549 default: /* Do nothing */
553 blank_color
= cons
->c_blank
;
554 mem_vid_copy(BLANK_MEM
, dst
, count
);
557 case 'K': /* ESC [sK clears line from cursor */
559 case 0: /* Clear from cursor to end of line */
560 count
= scr_width
- cons
->c_column
;
563 case 1: /* Clear from beginning of line to cursor */
564 count
= cons
->c_column
;
565 dst
= cons
->c_cur
- cons
->c_column
;
567 case 2: /* Clear entire line */
569 dst
= cons
->c_cur
- cons
->c_column
;
571 default: /* Do nothing */
575 blank_color
= cons
->c_blank
;
576 mem_vid_copy(BLANK_MEM
, dst
, count
);
579 case 'L': /* ESC [nL inserts n lines at cursor */
582 if (n
> (scr_lines
- cons
->c_row
))
583 n
= scr_lines
- cons
->c_row
;
585 src
= cons
->c_org
+ cons
->c_row
* scr_width
;
586 dst
= src
+ n
* scr_width
;
587 count
= (scr_lines
- cons
->c_row
- n
) * scr_width
;
588 vid_vid_copy(src
, dst
, count
);
589 blank_color
= cons
->c_blank
;
590 mem_vid_copy(BLANK_MEM
, src
, n
* scr_width
);
593 case 'M': /* ESC [nM deletes n lines at cursor */
596 if (n
> (scr_lines
- cons
->c_row
))
597 n
= scr_lines
- cons
->c_row
;
599 dst
= cons
->c_org
+ cons
->c_row
* scr_width
;
600 src
= dst
+ n
* scr_width
;
601 count
= (scr_lines
- cons
->c_row
- n
) * scr_width
;
602 vid_vid_copy(src
, dst
, count
);
603 blank_color
= cons
->c_blank
;
604 mem_vid_copy(BLANK_MEM
, dst
+ count
, n
* scr_width
);
607 case '@': /* ESC [n@ inserts n chars at cursor */
610 if (n
> (scr_width
- cons
->c_column
))
611 n
= scr_width
- cons
->c_column
;
615 count
= scr_width
- cons
->c_column
- n
;
616 vid_vid_copy(src
, dst
, count
);
617 blank_color
= cons
->c_blank
;
618 mem_vid_copy(BLANK_MEM
, src
, n
);
621 case 'P': /* ESC [nP deletes n chars at cursor */
624 if (n
> (scr_width
- cons
->c_column
))
625 n
= scr_width
- cons
->c_column
;
629 count
= scr_width
- cons
->c_column
- n
;
630 vid_vid_copy(src
, dst
, count
);
631 blank_color
= cons
->c_blank
;
632 mem_vid_copy(BLANK_MEM
, dst
+ count
, n
);
635 case 'm': /* ESC [nm enables rendition n */
636 for (parmp
= cons
->c_esc_parmv
; parmp
<= cons
->c_esc_parmp
637 && parmp
< bufend(cons
->c_esc_parmv
); parmp
++) {
638 if (cons
->c_reverse
) {
639 /* Unswap fg and bg colors */
640 cons
->c_attr
= ((cons
->c_attr
& 0x7000) >> 4) |
641 ((cons
->c_attr
& 0x0700) << 4) |
642 ((cons
->c_attr
& 0x8800));
644 switch (n
= *parmp
) {
646 cons
->c_attr
= cons
->c_blank
= BLANK_COLOR
;
647 cons
->c_reverse
= FALSE
;
651 /* Set intensity bit */
652 cons
->c_attr
|= 0x0800;
655 case 4: /* UNDERLINE */
657 /* Change white to cyan, i.e. lose red
659 cons
->c_attr
= (cons
->c_attr
& 0xBBFF);
661 /* Set underline attribute */
662 cons
->c_attr
= (cons
->c_attr
& 0x99FF);
666 case 5: /* BLINKING */
667 /* Set the blink bit */
668 cons
->c_attr
|= 0x8000;
671 case 7: /* REVERSE */
672 cons
->c_reverse
= TRUE
;
676 if (n
== 39) n
= 37; /* set default color */
680 /* Don't mess up a monochrome screen */
682 if (30 <= n
&& n
<= 37) {
683 /* Foreground color */
685 (cons
->c_attr
& 0xF8FF) |
686 (ansi_colors
[(n
- 30)] << 8);
688 (cons
->c_blank
& 0xF8FF) |
689 (ansi_colors
[(n
- 30)] << 8);
691 if (40 <= n
&& n
<= 47) {
692 /* Background color */
694 (cons
->c_attr
& 0x8FFF) |
695 (ansi_colors
[(n
- 40)] << 12);
697 (cons
->c_blank
& 0x8FFF) |
698 (ansi_colors
[(n
- 40)] << 12);
701 if (cons
->c_reverse
) {
702 /* Swap fg and bg colors */
703 cons
->c_attr
= ((cons
->c_attr
& 0x7000) >> 4) |
704 ((cons
->c_attr
& 0x0700) << 4) |
705 ((cons
->c_attr
& 0x8800));
711 cons
->c_esc_state
= 0;
714 /*===========================================================================*
716 *===========================================================================*/
717 PRIVATE
void set_6845(reg
, val
)
718 int reg
; /* which register pair to set */
719 unsigned val
; /* 16-bit value to set it to */
721 /* Set a register pair inside the 6845.
722 * Registers 12-13 tell the 6845 where in video ram to start
723 * Registers 14-15 tell the 6845 where to put the cursor
725 pvb_pair_t char_out
[4];
726 pv_set(char_out
[0], vid_port
+ INDEX
, reg
); /* set index register */
727 pv_set(char_out
[1], vid_port
+ DATA
, (val
>>8) & BYTE
); /* high byte */
728 pv_set(char_out
[2], vid_port
+ INDEX
, reg
+ 1); /* again */
729 pv_set(char_out
[3], vid_port
+ DATA
, val
&BYTE
); /* low byte */
730 sys_voutb(char_out
, 4); /* do actual output */
733 /*===========================================================================*
735 *===========================================================================*/
736 PRIVATE
void get_6845(reg
, val
)
737 int reg
; /* which register pair to set */
738 unsigned *val
; /* 16-bit value to set it to */
742 /* Get a register pair inside the 6845. */
743 sys_outb(vid_port
+ INDEX
, reg
);
744 sys_inb(vid_port
+ DATA
, &v
);
746 sys_outb(vid_port
+ INDEX
, reg
+1);
747 sys_inb(vid_port
+ DATA
, &v
);
749 *val
= (v1
<< 8) | v2
;
752 /*===========================================================================*
754 *===========================================================================*/
757 /* Making a beeping sound on the speaker (output for CRTL-G).
758 * This routine works by turning on the bits 0 and 1 in port B of the 8255
759 * chip that drive the speaker.
761 static timer_t tmr_stop_beep
;
762 pvb_pair_t char_out
[3];
764 unsigned long port_b_val
;
767 /* Fetch current time in advance to prevent beeping delay. */
768 if ((s
=getuptime(&now
)) != OK
)
769 panic("TTY","Console couldn't get clock's uptime.", s
);
771 /* Set timer channel 2, square wave, with given frequency. */
772 pv_set(char_out
[0], TIMER_MODE
, 0xB6);
773 pv_set(char_out
[1], TIMER2
, (BEEP_FREQ
>> 0) & BYTE
);
774 pv_set(char_out
[2], TIMER2
, (BEEP_FREQ
>> 8) & BYTE
);
775 if (sys_voutb(char_out
, 3)==OK
) {
776 if (sys_inb(PORT_B
, &port_b_val
)==OK
&&
777 sys_outb(PORT_B
, (port_b_val
|3))==OK
)
781 /* Add a timer to the timers list. Possibly reschedule the alarm. */
782 tmrs_settimer(&tty_timers
, &tmr_stop_beep
, now
+B_TIME
, stop_beep
, NULL
);
783 if (tty_timers
->tmr_exp_time
!= tty_next_timeout
) {
784 tty_next_timeout
= tty_timers
->tmr_exp_time
;
785 if ((s
=sys_setalarm(tty_next_timeout
, 1)) != OK
)
786 panic("TTY","Console couldn't set alarm.", s
);
791 /*===========================================================================*
793 *===========================================================================*/
794 PUBLIC
void do_video(message
*m
)
796 int i
, n
, r
, ops
, watch
, safe
= 0;
799 /* Execute the requested device driver function. */
800 r
= EINVAL
; /* just in case */
803 /* Should grant IOPL */
813 if (m
->TTY_REQUEST
== MIOCMAP
|| m
->TTY_REQUEST
== MIOCUNMAP
)
816 struct mapreq mapreq
;
818 do_map
= (m
->REQUEST
== MIOCMAP
); /* else unmap */
820 /* Get request structure */
822 r
= sys_safecopyfrom(m
->IO_ENDPT
,
823 (vir_bytes
)m
->ADDRESS
, 0, (vir_bytes
) &mapreq
,
826 r
= sys_vircopy(m
->IO_ENDPT
, D
,
827 (vir_bytes
)m
->ADDRESS
,
828 SELF
, D
, (vir_bytes
)&mapreq
, sizeof(mapreq
));
832 tty_reply(TASK_REPLY
, m
->m_source
, m
->IO_ENDPT
,
837 /* In safe ioctl mode, the POSITION field contains
838 * the endpt number of the original requestor.
839 * IO_ENDPT is always FS.
842 r
= sys_vm_map(safe
? m
->POSITION
: m
->IO_ENDPT
,
843 do_map
, (phys_bytes
)mapreq
.base
, mapreq
.size
,
845 tty_reply(TASK_REPLY
, m
->m_source
, m
->IO_ENDPT
, r
);
853 "Warning, TTY(video) got unexpected request %d from %d\n",
854 m
->m_type
, m
->m_source
);
857 tty_reply(TASK_REPLY
, m
->m_source
, m
->IO_ENDPT
, r
);
861 /*===========================================================================*
863 *===========================================================================*/
864 PUBLIC
void beep_x(freq
, dur
)
868 /* Making a beeping sound on the speaker.
869 * This routine works by turning on the bits 0 and 1 in port B of the 8255
870 * chip that drive the speaker.
872 static timer_t tmr_stop_beep
;
873 pvb_pair_t char_out
[3];
875 unsigned long port_b_val
;
878 unsigned long ival
= TIMER_FREQ
/ freq
;
879 if (ival
== 0 || ival
> 0xffff)
880 return; /* Frequency out of range */
882 /* Fetch current time in advance to prevent beeping delay. */
883 if ((s
=getuptime(&now
)) != OK
)
884 panic("TTY","Console couldn't get clock's uptime.", s
);
886 /* Set timer channel 2, square wave, with given frequency. */
887 pv_set(char_out
[0], TIMER_MODE
, 0xB6);
888 pv_set(char_out
[1], TIMER2
, (ival
>> 0) & BYTE
);
889 pv_set(char_out
[2], TIMER2
, (ival
>> 8) & BYTE
);
890 if (sys_voutb(char_out
, 3)==OK
) {
891 if (sys_inb(PORT_B
, &port_b_val
)==OK
&&
892 sys_outb(PORT_B
, (port_b_val
|3))==OK
)
896 /* Add a timer to the timers list. Possibly reschedule the alarm. */
897 tmrs_settimer(&tty_timers
, &tmr_stop_beep
, now
+dur
, stop_beep
, NULL
);
898 if (tty_timers
->tmr_exp_time
!= tty_next_timeout
) {
899 tty_next_timeout
= tty_timers
->tmr_exp_time
;
900 if ((s
=sys_setalarm(tty_next_timeout
, 1)) != OK
)
901 panic("TTY","Console couldn't set alarm.", s
);
905 /*===========================================================================*
907 *===========================================================================*/
908 PRIVATE
void stop_beep(tmrp
)
911 /* Turn off the beeper by turning off bits 0 and 1 in PORT_B. */
912 unsigned long port_b_val
;
913 if (sys_inb(PORT_B
, &port_b_val
)==OK
&&
914 sys_outb(PORT_B
, (port_b_val
& ~3))==OK
)
918 /*===========================================================================*
920 *===========================================================================*/
921 PUBLIC
void scr_init(tp
)
924 /* Initialize the screen driver. */
927 u16_t bios_columns
, bios_crtbase
, bios_fontlines
;
931 static int vdu_initialized
= 0;
934 /* Associate console and TTY. */
935 line
= tp
- &tty_table
[0];
936 if (line
>= nr_cons
) return;
937 cons
= &cons_table
[line
];
941 /* Fill in TTY function hooks. */
942 tp
->tty_devwrite
= cons_write
;
943 tp
->tty_echo
= cons_echo
;
944 tp
->tty_ioctl
= cons_ioctl
;
946 /* Get the BIOS parameters that describe the VDU. */
947 if (! vdu_initialized
++) {
949 /* How about error checking? What to do on failure??? */
950 s
=sys_readbios(VDU_SCREEN_COLS_ADDR
, &bios_columns
,
951 VDU_SCREEN_COLS_SIZE
);
952 s
=sys_readbios(VDU_CRT_BASE_ADDR
, &bios_crtbase
,
954 s
=sys_readbios( VDU_SCREEN_ROWS_ADDR
, &bios_rows
,
955 VDU_SCREEN_ROWS_SIZE
);
956 s
=sys_readbios(VDU_FONTLINES_ADDR
, &bios_fontlines
,
959 vid_port
= bios_crtbase
;
960 scr_width
= bios_columns
;
961 font_lines
= bios_fontlines
;
962 scr_lines
= machine
.vdu_ega
? bios_rows
+1 : 25;
965 vid_base
= COLOR_BASE
;
966 vid_size
= COLOR_SIZE
;
968 vid_base
= MONO_BASE
;
969 vid_size
= MONO_SIZE
;
971 if (machine
.vdu_ega
) vid_size
= EGA_SIZE
;
972 wrap
= ! machine
.vdu_ega
;
974 s
= sys_segctl(&vid_index
, &vid_seg
, &vid_off
, vid_base
, vid_size
);
976 vid_size
>>= 1; /* word count */
977 vid_mask
= vid_size
- 1;
979 /* Size of the screen (number of displayed characters.) */
980 scr_size
= scr_lines
* scr_width
;
982 /* There can be as many consoles as video memory allows. */
983 nr_cons
= vid_size
/ scr_size
;
984 if (nr_cons
> NR_CONS
) nr_cons
= NR_CONS
;
985 if (nr_cons
> 1) wrap
= 0;
986 page_size
= vid_size
/ nr_cons
;
989 cons
->c_start
= line
* page_size
;
990 cons
->c_limit
= cons
->c_start
+ page_size
;
991 cons
->c_cur
= cons
->c_org
= cons
->c_start
;
992 cons
->c_attr
= cons
->c_blank
= BLANK_COLOR
;
995 /* Clear the non-console vtys. */
996 blank_color
= BLANK_COLOR
;
997 mem_vid_copy(BLANK_MEM
, cons
->c_start
, scr_size
);
1000 /* Set the cursor of the console vty at the bottom. c_cur
1001 * is updated automatically later.
1003 scroll_screen(cons
, SCROLL_UP
);
1004 cons
->c_row
= scr_lines
- 1;
1011 /*===========================================================================*
1013 *===========================================================================*/
1014 PUBLIC
void kputc(c
)
1017 /* Accumulate a single character for a kernel message. Send a notification
1018 * the to output driver if an END_OF_KMESS is encountered.
1030 kmess
.km_buf
[kmess
.km_next
] = c
; /* put normal char in buffer */
1031 if (kmess
.km_size
< KMESS_BUF_SIZE
)
1033 kmess
.km_next
= (kmess
.km_next
+ 1) % KMESS_BUF_SIZE
;
1035 notify(LOG_PROC_NR
);
1039 /*===========================================================================*
1041 *===========================================================================*/
1042 PUBLIC
void do_new_kmess(m
)
1045 /* Notification for a new kernel message. */
1046 struct kmessages kmess
; /* kmessages structure */
1047 static int prev_next
= 0; /* previous next seen */
1052 /* Try to get a fresh copy of the buffer with kernel messages. */
1054 /* During shutdown, the reply is garbled because new notifications arrive
1055 * while the system task makes a copy of the kernel messages buffer.
1056 * Hence, don't check the return value.
1058 if ((r
=sys_getkmessages(&kmess
)) != OK
) {
1059 printf("TTY: couldn't get copy of kmessages: %d, 0x%x\n", r
,r
);
1063 sys_getkmessages(&kmess
);
1065 /* Print only the new part. Determine how many new bytes there are with
1066 * help of the current and previous 'next' index. Note that the kernel
1067 * buffer is circular. This works fine if less then KMESS_BUF_SIZE bytes
1068 * is new data; else we miss % KMESS_BUF_SIZE here.
1069 * Check for size being positive, the buffer might as well be emptied!
1071 if (kmess
.km_size
> 0) {
1072 bytes
= ((kmess
.km_next
+ KMESS_BUF_SIZE
) - prev_next
) % KMESS_BUF_SIZE
;
1073 r
=prev_next
; /* start at previous old */
1075 cons_putk( kmess
.km_buf
[(r
%KMESS_BUF_SIZE
)] );
1079 cons_putk(0); /* terminate to flush output */
1082 /* Almost done, store 'next' so that we can determine what part of the
1083 * kernel messages buffer to print next time a notification arrives.
1085 prev_next
= kmess
.km_next
;
1088 /*===========================================================================*
1090 *===========================================================================*/
1091 PUBLIC
void do_diagnostics(m_ptr
, safe
)
1092 message
*m_ptr
; /* pointer to request message */
1095 /* Print a string for a server. */
1098 int count
, offset
= 0;
1100 int proc_nr
= m_ptr
->m_source
;
1102 src
= (vir_bytes
) m_ptr
->DIAG_PRINT_BUF_G
;
1103 for (count
= m_ptr
->DIAG_BUF_COUNT
; count
> 0; count
--) {
1106 r
= sys_safecopyfrom(proc_nr
, src
, offset
, (vir_bytes
) &c
, 1, D
);
1108 r
= sys_vircopy(proc_nr
, D
, src
+offset
, SELF
, D
, (vir_bytes
) &c
, 1);
1117 cons_putk(0); /* always terminate, even with EFAULT */
1118 m_ptr
->m_type
= result
;
1119 send(m_ptr
->m_source
, m_ptr
);
1122 /*===========================================================================*
1124 *===========================================================================*/
1125 PUBLIC
void do_get_kmess(m_ptr
)
1126 message
*m_ptr
; /* pointer to request message */
1128 /* Provide the log device with debug output */
1132 dst
= (vir_bytes
) m_ptr
->GETKM_PTR
;
1134 if (sys_vircopy(SELF
, D
, (vir_bytes
)&kmess
, m_ptr
->m_source
, D
,
1135 dst
, sizeof(kmess
)) != OK
) {
1139 send(m_ptr
->m_source
, m_ptr
);
1142 /*===========================================================================*
1144 *===========================================================================*/
1145 PUBLIC
void do_get_kmess_s(m_ptr
)
1146 message
*m_ptr
; /* pointer to request message */
1148 /* Provide the log device with debug output */
1152 gid
= m_ptr
->GETKM_GRANT
;
1154 if (sys_safecopyto(m_ptr
->m_source
, gid
, 0, (vir_bytes
)&kmess
, sizeof(kmess
),
1159 send(m_ptr
->m_source
, m_ptr
);
1162 /*===========================================================================*
1164 *===========================================================================*/
1165 PRIVATE
void cons_putk(c
)
1166 int c
; /* character to print */
1168 /* This procedure is used to print a character on the console.
1171 if (c
== '\n') cons_putk('\r');
1172 out_char(&cons_table
[0], (int) c
);
1174 flush(&cons_table
[0]);
1178 /*===========================================================================*
1180 *===========================================================================*/
1181 PUBLIC
void toggle_scroll()
1183 /* Toggle between hardware and software scroll. */
1186 softscroll
= !softscroll
;
1187 printf("%sware scrolling enabled.\n", softscroll
? "Soft" : "Hard");
1190 /*===========================================================================*
1192 *===========================================================================*/
1193 PUBLIC
void cons_stop()
1195 /* Prepare for halt or reboot. */
1199 cons_table
[0].c_attr
= cons_table
[0].c_blank
= BLANK_COLOR
;
1202 /*===========================================================================*
1204 *===========================================================================*/
1205 PRIVATE
void cons_org0()
1207 /* Scroll video memory back to put the origin at 0. */
1212 for (cons_line
= 0; cons_line
< nr_cons
; cons_line
++) {
1213 cons
= &cons_table
[cons_line
];
1214 while (cons
->c_org
> cons
->c_start
) {
1215 n
= vid_size
- scr_size
; /* amount of unused memory */
1216 if (n
> cons
->c_org
- cons
->c_start
)
1217 n
= cons
->c_org
- cons
->c_start
;
1218 vid_vid_copy(cons
->c_org
, cons
->c_org
- n
, scr_size
);
1223 select_console(ccurrent
);
1226 /*===========================================================================*
1228 *===========================================================================*/
1229 PRIVATE
void disable_console()
1231 if (disabled_vc
!= -1)
1234 disabled_vc
= ccurrent
;
1235 disabled_sm
= softscroll
;
1241 /* Should also disable further output to virtual consoles */
1244 /*===========================================================================*
1245 * reenable_console *
1246 *===========================================================================*/
1247 PRIVATE
void reenable_console()
1249 if (disabled_vc
== -1)
1252 softscroll
= disabled_sm
;
1253 select_console(disabled_vc
);
1257 /*===========================================================================*
1259 *===========================================================================*/
1260 PUBLIC
void select_console(int cons_line
)
1262 /* Set the current console to console number 'cons_line'. */
1264 if (cons_line
< 0 || cons_line
>= nr_cons
) return;
1265 ccurrent
= cons_line
;
1266 curcons
= &cons_table
[cons_line
];
1267 set_6845(VID_ORG
, curcons
->c_org
);
1268 set_6845(CURSOR
, curcons
->c_cur
);
1271 /*===========================================================================*
1273 *===========================================================================*/
1274 PUBLIC
int con_loadfont(m
)
1277 /* Load a font into the EGA or VGA adapter. */
1279 static struct sequence seq1
[7] = {
1280 { GA_SEQUENCER_INDEX
, 0x00, 0x01 },
1281 { GA_SEQUENCER_INDEX
, 0x02, 0x04 },
1282 { GA_SEQUENCER_INDEX
, 0x04, 0x07 },
1283 { GA_SEQUENCER_INDEX
, 0x00, 0x03 },
1284 { GA_GRAPHICS_INDEX
, 0x04, 0x02 },
1285 { GA_GRAPHICS_INDEX
, 0x05, 0x00 },
1286 { GA_GRAPHICS_INDEX
, 0x06, 0x00 },
1288 static struct sequence seq2
[7] = {
1289 { GA_SEQUENCER_INDEX
, 0x00, 0x01 },
1290 { GA_SEQUENCER_INDEX
, 0x02, 0x03 },
1291 { GA_SEQUENCER_INDEX
, 0x04, 0x03 },
1292 { GA_SEQUENCER_INDEX
, 0x00, 0x03 },
1293 { GA_GRAPHICS_INDEX
, 0x04, 0x00 },
1294 { GA_GRAPHICS_INDEX
, 0x05, 0x10 },
1295 { GA_GRAPHICS_INDEX
, 0x06, 0 },
1298 seq2
[6].value
= color
? 0x0E : 0x0A;
1300 if (!machine
.vdu_ega
) return(ENOTTY
);
1301 result
= ga_program(seq1
); /* bring font memory into view */
1303 result
= sys_physcopy(m
->IO_ENDPT
, GRANT_SEG
, (vir_bytes
) m
->ADDRESS
,
1304 NONE
, PHYS_SEG
, (phys_bytes
) GA_VIDEO_ADDRESS
, (phys_bytes
)GA_FONT_SIZE
);
1306 result
= ga_program(seq2
); /* restore */
1311 /*===========================================================================*
1313 *===========================================================================*/
1314 PRIVATE
int ga_program(seq
)
1315 struct sequence
*seq
;
1317 pvb_pair_t char_out
[14];
1319 for (i
=0; i
<7; i
++) {
1320 pv_set(char_out
[2*i
], seq
->index
, seq
->port
);
1321 pv_set(char_out
[2*i
+1], seq
->index
+1, seq
->value
);
1324 return sys_voutb(char_out
, 14);
1327 /*===========================================================================*
1329 *===========================================================================*/
1330 PRIVATE
int cons_ioctl(tp
, try)
1334 /* Set the screen dimensions. */
1336 tp
->tty_winsize
.ws_row
= scr_lines
;
1337 tp
->tty_winsize
.ws_col
= scr_width
;
1338 tp
->tty_winsize
.ws_xpixel
= scr_width
* 8;
1339 tp
->tty_winsize
.ws_ypixel
= scr_lines
* font_lines
;
1342 #define COM1_BASE 0x3F8
1343 #define COM1_THR (COM1_BASE + 0)
1344 #define LSR_THRE 0x20
1345 #define COM1_LSR (COM1_BASE + 5)
1347 PRIVATE
void ser_putc(char c
)
1355 for (i
= 0; i
<100; i
++)