1 /* See COPYRIGHT for copyright information. */
4 #include <inc/memlayout.h>
5 #include <inc/kbdreg.h>
6 #include <inc/string.h>
7 #include <inc/assert.h>
9 #include <kern/console.h>
10 #include <kern/picirq.h>
12 static void cons_intr(int (*proc
)(void));
13 static void cons_putc(int c
);
15 // Stupid I/O delay routine necessitated by historical PC design flaws
25 /***** Serial I/O code *****/
29 #define COM_RX 0 // In: Receive buffer (DLAB=0)
30 #define COM_TX 0 // Out: Transmit buffer (DLAB=0)
31 #define COM_DLL 0 // Out: Divisor Latch Low (DLAB=1)
32 #define COM_DLM 1 // Out: Divisor Latch High (DLAB=1)
33 #define COM_IER 1 // Out: Interrupt Enable Register
34 #define COM_IER_RDI 0x01 // Enable receiver data interrupt
35 #define COM_IIR 2 // In: Interrupt ID Register
36 #define COM_FCR 2 // Out: FIFO Control Register
37 #define COM_LCR 3 // Out: Line Control Register
38 #define COM_LCR_DLAB 0x80 // Divisor latch access bit
39 #define COM_LCR_WLEN8 0x03 // Wordlength: 8 bits
40 #define COM_MCR 4 // Out: Modem Control Register
41 #define COM_MCR_RTS 0x02 // RTS complement
42 #define COM_MCR_DTR 0x01 // DTR complement
43 #define COM_MCR_OUT2 0x08 // Out2 complement
44 #define COM_LSR 5 // In: Line Status Register
45 #define COM_LSR_DATA 0x01 // Data available
46 #define COM_LSR_TXRDY 0x20 // Transmit buffer avail
47 #define COM_LSR_TSRE 0x40 // Transmitter off
49 static bool serial_exists
;
52 serial_proc_data(void)
54 if (!(inb(COM1
+COM_LSR
) & COM_LSR_DATA
))
56 return inb(COM1
+COM_RX
);
63 cons_intr(serial_proc_data
);
72 !(inb(COM1
+ COM_LSR
) & COM_LSR_TXRDY
) && i
< 12800;
76 outb(COM1
+ COM_TX
, c
);
83 outb(COM1
+COM_FCR
, 0);
85 // Set speed; requires DLAB latch
86 outb(COM1
+COM_LCR
, COM_LCR_DLAB
);
87 outb(COM1
+COM_DLL
, (uint8_t) (115200 / 9600));
88 outb(COM1
+COM_DLM
, 0);
90 // 8 data bits, 1 stop bit, parity off; turn off DLAB latch
91 outb(COM1
+COM_LCR
, COM_LCR_WLEN8
& ~COM_LCR_DLAB
);
94 outb(COM1
+COM_MCR
, 0);
95 // Enable rcv interrupts
96 outb(COM1
+COM_IER
, COM_IER_RDI
);
98 // Clear any preexisting overrun indications and interrupts
99 // Serial port doesn't exist if COM_LSR returns 0xFF
100 serial_exists
= (inb(COM1
+COM_LSR
) != 0xFF);
101 (void) inb(COM1
+COM_IIR
);
102 (void) inb(COM1
+COM_RX
);
108 /***** Parallel port output code *****/
109 // For information on PC parallel port programming, see the class References
117 for (i
= 0; !(inb(0x378+1) & 0x80) && i
< 12800; i
++)
120 outb(0x378+2, 0x08|0x04|0x01);
127 /***** Text-mode CGA/VGA display output *****/
129 static unsigned addr_6845
;
130 static uint16_t *crt_buf
;
131 static uint16_t crt_pos
;
133 // Scrollback support
134 #define CRT_SAVEROWS 128
137 static uint16_t crtsave_buf
[CRT_SAVEROWS
* CRT_COLS
];
138 static uint16_t crtsave_pos
;
139 static int16_t crtsave_backscroll
;
140 static uint16_t crtsave_size
;
146 volatile uint16_t *cp
;
150 cp
= (uint16_t*) (KERNBASE
+ CGA_BUF
);
152 *cp
= (uint16_t) 0xA55A;
154 cp
= (uint16_t*) (KERNBASE
+ MONO_BUF
);
155 addr_6845
= MONO_BASE
;
158 addr_6845
= CGA_BASE
;
161 /* Extract cursor location */
163 pos
= inb(addr_6845
+ 1) << 8;
165 pos
|= inb(addr_6845
+ 1);
167 crt_buf
= (uint16_t*) cp
;
173 // Copy one screen's worth of data to or from the save buffer,
174 // starting at line 'first_line'.
176 cga_savebuf_copy(int first_line
, bool to_screen
)
182 // Calculate the beginning & end of the save buffer area.
183 pos
= crtsave_buf
+ (first_line
% CRT_SAVEROWS
) * CRT_COLS
;
184 end
= pos
+ CRT_ROWS
* CRT_COLS
;
185 // Check for wraparound.
186 trueend
= MIN(end
, crtsave_buf
+ CRT_SAVEROWS
* CRT_COLS
);
188 // Copy the initial portion.
190 memmove(crt_buf
, pos
, (trueend
- pos
) * sizeof(uint16_t));
192 memmove(pos
, crt_buf
, (trueend
- pos
) * sizeof(uint16_t));
194 // If there was wraparound, copy the second part of the screen.
198 memmove(crt_buf
+ (trueend
- pos
), crtsave_buf
, (end
- trueend
) * sizeof(uint16_t));
200 memmove(crtsave_buf
, crt_buf
+ (trueend
- pos
), (end
- trueend
) * sizeof(uint16_t));
209 // unscroll if necessary
210 if (crtsave_backscroll
> 0) {
211 cga_savebuf_copy(crtsave_pos
+ crtsave_size
, 1);
212 crtsave_backscroll
= 0;
216 // if no attribute given, then use black on white
224 crt_buf
[crt_pos
] = (c
& ~0xff) | ' ';
231 crt_pos
-= (crt_pos
% CRT_COLS
);
241 crt_buf
[crt_pos
++] = c
; /* write the character */
245 // What is the purpose of this?
246 if (crt_pos
>= CRT_SIZE
) {
250 // Save the scrolled-back row
251 if (crtsave_size
== CRT_SAVEROWS
- CRT_ROWS
)
252 crtsave_pos
= (crtsave_pos
+ 1) % CRT_SAVEROWS
;
255 memmove(crtsave_buf
+ ((crtsave_pos
+ crtsave_size
- 1) % CRT_SAVEROWS
) * CRT_COLS
, crt_buf
, CRT_COLS
* sizeof(uint16_t));
258 memmove(crt_buf
, crt_buf
+ CRT_COLS
, (CRT_SIZE
- CRT_COLS
) * sizeof(uint16_t));
259 for (i
= CRT_SIZE
- CRT_COLS
; i
< CRT_SIZE
; i
++)
260 crt_buf
[i
] = 0x0700 | ' ';
264 /* move that little blinky thing */
266 outb(addr_6845
+ 1, crt_pos
>> 8);
268 outb(addr_6845
+ 1, crt_pos
);
273 cga_scroll(int delta
)
275 int new_backscroll
= MAX(MIN(crtsave_backscroll
- delta
, crtsave_size
), 0);
277 if (new_backscroll
== crtsave_backscroll
)
279 if (crtsave_backscroll
== 0)
280 // save current screen
281 cga_savebuf_copy(crtsave_pos
+ crtsave_size
, 0);
283 crtsave_backscroll
= new_backscroll
;
284 cga_savebuf_copy(crtsave_pos
+ crtsave_size
- crtsave_backscroll
, 1);
289 /***** Keyboard input code *****/
297 #define CAPSLOCK (1<<3)
298 #define NUMLOCK (1<<4)
299 #define SCROLLLOCK (1<<5)
303 // Synonyms of other keys for the numeric keypad
304 #define KEY_KP_ENTER '\n'
305 #define KEY_KP_DIV '/'
307 static const uint8_t shiftcode
[256] =
309 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00
310 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, CTL
, 0, 0, // 0x10
311 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SHIFT
, 0, 0, 0, 0, 0, // 0x20
312 0, 0, 0, 0, 0, 0, SHIFT
, 0, ALT
, 0, 0, 0, 0, 0, 0, 0, // 0x30
313 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40
314 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x50
315 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60
316 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x70
317 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80
318 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, CTL
, 0, 0, // 0x90
319 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0
320 0, 0, 0, 0, 0, 0, 0, 0, ALT
, 0, 0, 0, 0, 0, 0, 0, // 0xB0
321 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0
322 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0
323 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0
324 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0
327 static const uint8_t togglecode
[256] =
329 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00
330 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10
331 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20
332 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, CAPSLOCK
, 0, 0, 0, 0, 0, // 0x30
333 0, 0, 0, 0, 0, NUMLOCK
, SCROLLLOCK
, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40
334 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x50
335 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60
336 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x70
337 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80
338 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90
339 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0
340 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0
341 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0
342 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0
343 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0
344 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0
347 static const uint8_t normalmap
[256] =
349 0, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
350 '7', '8', '9', '0', '-', '=', '\b', '\t',
351 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10
352 'o', 'p', '[', ']', '\n', 0, 'a', 's',
353 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20
354 '\'', '`', 0, '\\', 'z', 'x', 'c', 'v',
355 'b', 'n', 'm', ',', '.', '/', 0, '*', // 0x30
356 0, ' ', 0, 0, 0, 0, 0, 0,
357 0, 0, 0, 0, 0, 0, 0, '7', // 0x40
358 '8', '9', '-', '4', '5', '6', '+', '1',
359 '2', '3', '0', '.', 0, 0, 0, 0, // 0x50
360 0, 0, 0, 0, 0, 0, 0, 0,
361 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60
362 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x70
363 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80
364 0, 0, 0, 0, 0, 0, 0, 0, // 0x90
365 0, 0, 0, 0, KEY_KP_ENTER
, 0, 0, 0,
366 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0
367 0, 0, 0, 0, 0, KEY_KP_DIV
, 0, 0, // 0xB0
368 0, 0, 0, 0, 0, 0, 0, 0,
369 0, 0, 0, 0, 0, 0, 0, KEY_HOME
, // 0xC0
370 KEY_UP
, KEY_PGUP
, 0, KEY_LF
, 0, KEY_RT
, 0, KEY_END
,
371 KEY_DN
, KEY_PGDN
, KEY_INS
, KEY_DEL
, 0, 0, 0, 0, // 0xD0
372 0, 0, 0, 0, 0, 0, 0, 0,
373 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0
374 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0
377 static const uint8_t shiftmap
[256] =
379 0, 033, '!', '@', '#', '$', '%', '^', // 0x00
380 '&', '*', '(', ')', '_', '+', '\b', '\t',
381 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10
382 'O', 'P', '{', '}', '\n', 0, 'A', 'S',
383 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20
384 '"', '~', 0, '|', 'Z', 'X', 'C', 'V',
385 'B', 'N', 'M', '<', '>', '?', 0, '*', // 0x30
386 0, ' ', 0, 0, 0, 0, 0, 0,
387 0, 0, 0, 0, 0, 0, 0, '7', // 0x40
388 '8', '9', '-', '4', '5', '6', '+', '1',
389 '2', '3', '0', '.', 0, 0, 0, 0, // 0x50
390 0, 0, 0, 0, 0, 0, 0, 0,
391 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60
392 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x70
393 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80
394 0, 0, 0, 0, 0, 0, 0, KEY_HOME
, // 0x90
395 0, 0, 0, 0, KEY_KP_ENTER
, 0, 0, 0,
396 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0
397 0, 0, 0, 0, 0, KEY_KP_DIV
, 0, 0, // 0xB0
398 0, 0, 0, 0, 0, 0, 0, 0,
399 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0
400 KEY_UP
, KEY_PGUP
, 0, KEY_LF
, 0, KEY_RT
, 0, KEY_END
,
401 KEY_DN
, KEY_PGDN
, KEY_INS
, KEY_DEL
, 0, 0, 0, 0, // 0xD0
402 0, 0, 0, 0, 0, 0, 0, 0,
403 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0
404 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0
407 #define C(x) (x - '@')
409 static const uint8_t ctlmap
[256] =
411 0, 0, 0, 0, 0, 0, 0, 0, // 0x00
412 0, 0, 0, 0, 0, 0, 0, 0,
413 C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'), // 0x10
414 C('O'), C('P'), 0, 0, '\r', 0, C('A'), C('S'),
415 C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), 0, // 0x20
416 0, 0, 0, C('\\'), C('Z'), C('X'), C('C'), C('V'),
417 C('B'), C('N'), C('M'), 0, 0, C('/'), 0, 0, // 0x30
418 0, 0, 0, 0, 0, 0, 0, 0,
419 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40
420 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x50
421 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60
422 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x70
423 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80
424 0, 0, 0, 0, 0, 0, 0, KEY_HOME
, // 0x90
425 0, 0, 0, 0, 0, 0, 0, 0,
426 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0
427 0, 0, 0, 0, 0, C('/'), 0, 0, // 0xB0
428 0, 0, 0, 0, 0, 0, 0, 0,
429 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0
430 KEY_UP
, KEY_PGUP
, 0, KEY_LF
, 0, KEY_RT
, 0, KEY_END
,
431 KEY_DN
, KEY_PGDN
, KEY_INS
, KEY_DEL
, 0, 0, 0, 0, // 0xD0
432 0, 0, 0, 0, 0, 0, 0, 0,
433 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0
434 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0
437 static const uint8_t * const charcode
[4] = {
445 * Get data from the keyboard. If we finish a character, return it. Else 0.
446 * Return -1 if no data.
453 static uint32_t shift
;
455 if ((inb(KBSTATP
) & KBS_DIB
) == 0)
461 // E0 escape character
464 } else if (data
& 0x80) {
466 data
= (shift
& E0ESC
? data
: data
& 0x7F);
467 shift
&= ~(shiftcode
[data
] | E0ESC
);
469 } else if (shift
& E0ESC
) {
470 // Last character was an E0 escape; or with 0x80
475 shift
|= shiftcode
[data
];
476 shift
^= togglecode
[data
];
478 c
= charcode
[shift
& (CTL
| SHIFT
)][data
];
479 if (shift
& CAPSLOCK
) {
480 if ('a' <= c
&& c
<= 'z')
482 else if ('A' <= c
&& c
<= 'Z')
486 // Process special keys
488 // Shift-PageUp and Shift-PageDown: scroll console
489 if ((shift
& (CTL
| SHIFT
)) && (c
== KEY_PGUP
|| c
== KEY_PGDN
)) {
490 cga_scroll(c
== KEY_PGUP
? -CRT_ROWS
: CRT_ROWS
);
494 // Ctrl-Alt-Del: reboot
495 if (!(~shift
& (CTL
| ALT
)) && c
== KEY_DEL
) {
496 cprintf("Rebooting!\n");
497 outb(0x92, 0x3); // courtesy of Chris Frost
506 cons_intr(kbd_proc_data
);
512 // Drain the kbd buffer so that Bochs generates interrupts.
514 irq_setmask_8259A(irq_mask_8259A
& ~(1<<1));
519 /***** General device-independent console code *****/
520 // Here we manage the console input buffer,
521 // where we stash characters received from the keyboard or serial port
522 // whenever the corresponding interrupt occurs.
524 #define CONSBUFSIZE 512
527 uint8_t buf
[CONSBUFSIZE
];
532 // called by device interrupt routines to feed input characters
533 // into the circular console input buffer.
535 cons_intr(int (*proc
)(void))
539 while ((c
= (*proc
)()) != -1) {
542 cons
.buf
[cons
.wpos
++] = c
;
543 if (cons
.wpos
== CONSBUFSIZE
)
548 // return the next input character from the console, or 0 if none waiting
554 // poll for any pending input characters,
555 // so that this function works even when interrupts are disabled
556 // (e.g., when called from the kernel monitor).
560 // grab the next character from the input buffer.
561 if (cons
.rpos
!= cons
.wpos
) {
562 c
= cons
.buf
[cons
.rpos
++];
563 if (cons
.rpos
== CONSBUFSIZE
)
570 // output a character to the console
579 // initialize the console devices
588 cprintf("Serial port does not exist!\n");
592 // `High'-level console I/O. Used by readline and cprintf.
605 while ((c
= cons_getc()) == 0)