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>
12 void cons_intr(int (*proc
)(void));
15 /***** Serial I/O code *****/
19 #define COM_RX 0 // In: Receive buffer (DLAB=0)
20 #define COM_DLL 0 // Out: Divisor Latch Low (DLAB=1)
21 #define COM_DLM 1 // Out: Divisor Latch High (DLAB=1)
22 #define COM_IER 1 // Out: Interrupt Enable Register
23 #define COM_IER_RDI 0x01 // Enable receiver data interrupt
24 #define COM_IIR 2 // In: Interrupt ID Register
25 #define COM_FCR 2 // Out: FIFO Control Register
26 #define COM_LCR 3 // Out: Line Control Register
27 #define COM_LCR_DLAB 0x80 // Divisor latch access bit
28 #define COM_LCR_WLEN8 0x03 // Wordlength: 8 bits
29 #define COM_MCR 4 // Out: Modem Control Register
30 #define COM_MCR_RTS 0x02 // RTS complement
31 #define COM_MCR_DTR 0x01 // DTR complement
32 #define COM_MCR_OUT2 0x08 // Out2 complement
33 #define COM_LSR 5 // In: Line Status Register
34 #define COM_LSR_DATA 0x01 // Data available
36 static bool serial_exists
;
39 serial_proc_data(void)
41 if (!(inb(COM1
+COM_LSR
) & COM_LSR_DATA
))
43 return inb(COM1
+COM_RX
);
50 cons_intr(serial_proc_data
);
57 outb(COM1
+COM_FCR
, 0);
59 // Set speed; requires DLAB latch
60 outb(COM1
+COM_LCR
, COM_LCR_DLAB
);
61 outb(COM1
+COM_DLL
, (uint8_t) (115200 / 9600));
62 outb(COM1
+COM_DLM
, 0);
64 // 8 data bits, 1 stop bit, parity off; turn off DLAB latch
65 outb(COM1
+COM_LCR
, COM_LCR_WLEN8
& ~COM_LCR_DLAB
);
68 outb(COM1
+COM_MCR
, 0);
69 // Enable rcv interrupts
70 outb(COM1
+COM_IER
, COM_IER_RDI
);
72 // Clear any preexisting overrun indications and interrupts
73 // Serial port doesn't exist if COM_LSR returns 0xFF
74 serial_exists
= (inb(COM1
+COM_LSR
) != 0xFF);
75 (void) inb(COM1
+COM_IIR
);
76 (void) inb(COM1
+COM_RX
);
82 /***** Parallel port output code *****/
83 // For information on PC parallel port programming, see the class References
86 // Stupid I/O delay routine necessitated by historical PC design flaws
101 for (i
= 0; !(inb(0x378+1) & 0x80) && i
< 12800; i
++)
104 outb(0x378+2, 0x08|0x04|0x01);
111 /***** Text-mode CGA/VGA display output *****/
113 static unsigned addr_6845
;
114 static uint16_t *crt_buf
;
115 static uint16_t crt_pos
;
120 volatile uint16_t *cp
;
124 cp
= (uint16_t*) (KERNBASE
+ CGA_BUF
);
126 *cp
= (uint16_t) 0xA55A;
128 cp
= (uint16_t*) (KERNBASE
+ MONO_BUF
);
129 addr_6845
= MONO_BASE
;
132 addr_6845
= CGA_BASE
;
135 /* Extract cursor location */
137 pos
= inb(addr_6845
+ 1) << 8;
139 pos
|= inb(addr_6845
+ 1);
141 crt_buf
= (uint16_t*) cp
;
150 // if no attribute given, then use black on white
158 crt_buf
[crt_pos
] = (c
& ~0xff) | ' ';
165 crt_pos
-= (crt_pos
% CRT_COLS
);
175 crt_buf
[crt_pos
++] = c
; /* write the character */
179 // What is the purpose of this?
180 if (crt_pos
>= CRT_SIZE
) {
183 memmove(crt_buf
, crt_buf
+ CRT_COLS
, (CRT_SIZE
- CRT_COLS
) * sizeof(uint16_t));
184 for (i
= CRT_SIZE
- CRT_COLS
; i
< CRT_SIZE
; i
++)
185 crt_buf
[i
] = 0x0700 | ' ';
189 /* move that little blinky thing */
191 outb(addr_6845
+ 1, crt_pos
>> 8);
193 outb(addr_6845
+ 1, crt_pos
);
197 /***** Keyboard input code *****/
205 #define CAPSLOCK (1<<3)
206 #define NUMLOCK (1<<4)
207 #define SCROLLLOCK (1<<5)
211 static uint8_t shiftcode
[256] =
221 static uint8_t togglecode
[256] =
228 static uint8_t normalmap
[256] =
230 NO
, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
231 '7', '8', '9', '0', '-', '=', '\b', '\t',
232 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10
233 'o', 'p', '[', ']', '\n', NO
, 'a', 's',
234 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20
235 '\'', '`', NO
, '\\', 'z', 'x', 'c', 'v',
236 'b', 'n', 'm', ',', '.', '/', NO
, '*', // 0x30
237 NO
, ' ', NO
, NO
, NO
, NO
, NO
, NO
,
238 NO
, NO
, NO
, NO
, NO
, NO
, NO
, '7', // 0x40
239 '8', '9', '-', '4', '5', '6', '+', '1',
240 '2', '3', '0', '.', NO
, NO
, NO
, NO
, // 0x50
241 [0xC7] KEY_HOME
, [0x9C] '\n' /*KP_Enter*/,
242 [0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP
,
243 [0xC9] KEY_PGUP
, [0xCB] KEY_LF
,
244 [0xCD] KEY_RT
, [0xCF] KEY_END
,
245 [0xD0] KEY_DN
, [0xD1] KEY_PGDN
,
246 [0xD2] KEY_INS
, [0xD3] KEY_DEL
249 static uint8_t shiftmap
[256] =
251 NO
, 033, '!', '@', '#', '$', '%', '^', // 0x00
252 '&', '*', '(', ')', '_', '+', '\b', '\t',
253 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10
254 'O', 'P', '{', '}', '\n', NO
, 'A', 'S',
255 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20
256 '"', '~', NO
, '|', 'Z', 'X', 'C', 'V',
257 'B', 'N', 'M', '<', '>', '?', NO
, '*', // 0x30
258 NO
, ' ', NO
, NO
, NO
, NO
, NO
, NO
,
259 NO
, NO
, NO
, NO
, NO
, NO
, NO
, '7', // 0x40
260 '8', '9', '-', '4', '5', '6', '+', '1',
261 '2', '3', '0', '.', NO
, NO
, NO
, NO
, // 0x50
262 [0xC7] KEY_HOME
, [0x9C] '\n' /*KP_Enter*/,
263 [0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP
,
264 [0xC9] KEY_PGUP
, [0xCB] KEY_LF
,
265 [0xCD] KEY_RT
, [0xCF] KEY_END
,
266 [0xD0] KEY_DN
, [0xD1] KEY_PGDN
,
267 [0xD2] KEY_INS
, [0xD3] KEY_DEL
270 #define C(x) (x - '@')
272 static uint8_t ctlmap
[256] =
274 NO
, NO
, NO
, NO
, NO
, NO
, NO
, NO
,
275 NO
, NO
, NO
, NO
, NO
, NO
, NO
, NO
,
276 C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'),
277 C('O'), C('P'), NO
, NO
, '\r', NO
, C('A'), C('S'),
278 C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO
,
279 NO
, NO
, NO
, C('\\'), C('Z'), C('X'), C('C'), C('V'),
280 C('B'), C('N'), C('M'), NO
, NO
, C('/'), NO
, NO
,
282 [0xB5] C('/'), [0xC8] KEY_UP
,
283 [0xC9] KEY_PGUP
, [0xCB] KEY_LF
,
284 [0xCD] KEY_RT
, [0xCF] KEY_END
,
285 [0xD0] KEY_DN
, [0xD1] KEY_PGDN
,
286 [0xD2] KEY_INS
, [0xD3] KEY_DEL
289 static uint8_t *charcode
[4] = {
297 * Get data from the keyboard. If we finish a character, return it. Else 0.
298 * Return -1 if no data.
305 static uint32_t shift
;
307 if ((inb(KBSTATP
) & KBS_DIB
) == 0)
313 // E0 escape character
316 } else if (data
& 0x80) {
318 data
= (shift
& E0ESC
? data
: data
& 0x7F);
319 shift
&= ~(shiftcode
[data
] | E0ESC
);
321 } else if (shift
& E0ESC
) {
322 // Last character was an E0 escape; or with 0x80
327 shift
|= shiftcode
[data
];
328 shift
^= togglecode
[data
];
330 c
= charcode
[shift
& (CTL
| SHIFT
)][data
];
331 if (shift
& CAPSLOCK
) {
332 if ('a' <= c
&& c
<= 'z')
334 else if ('A' <= c
&& c
<= 'Z')
338 // Process special keys
339 // Ctrl-Alt-Del: reboot
340 if (!(~shift
& (CTL
| ALT
)) && c
== KEY_DEL
) {
341 cprintf("Rebooting!\n");
342 outb(0x92, 0x3); // courtesy of Chris Frost
351 cons_intr(kbd_proc_data
);
361 /***** General device-independent console code *****/
362 // Here we manage the console input buffer,
363 // where we stash characters received from the keyboard or serial port
364 // whenever the corresponding interrupt occurs.
366 #define CONSBUFSIZE 512
369 uint8_t buf
[CONSBUFSIZE
];
374 // called by device interrupt routines to feed input characters
375 // into the circular console input buffer.
377 cons_intr(int (*proc
)(void))
381 while ((c
= (*proc
)()) != -1) {
384 cons
.buf
[cons
.wpos
++] = c
;
385 if (cons
.wpos
== CONSBUFSIZE
)
390 // return the next input character from the console, or 0 if none waiting
396 // poll for any pending input characters,
397 // so that this function works even when interrupts are disabled
398 // (e.g., when called from the kernel monitor).
402 // grab the next character from the input buffer.
403 if (cons
.rpos
!= cons
.wpos
) {
404 c
= cons
.buf
[cons
.rpos
++];
405 if (cons
.rpos
== CONSBUFSIZE
)
412 // output a character to the console
420 // initialize the console devices
429 cprintf("Serial port does not exist!\n");
433 // `High'-level console I/O. Used by readline and cprintf.
446 while ((c
= cons_getc()) == 0)