initial commit
[mit-jos.git] / kern / console.c
blob150fe16e1cafd49a51f4d34f2d7485f3fcaa7114
1 /* See COPYRIGHT for copyright information. */
3 #include <inc/x86.h>
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 *****/
17 #define COM1 0x3F8
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;
38 int
39 serial_proc_data(void)
41 if (!(inb(COM1+COM_LSR) & COM_LSR_DATA))
42 return -1;
43 return inb(COM1+COM_RX);
46 void
47 serial_intr(void)
49 if (serial_exists)
50 cons_intr(serial_proc_data);
53 void
54 serial_init(void)
56 // Turn off the FIFO
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);
67 // No modem controls
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
84 // page.
86 // Stupid I/O delay routine necessitated by historical PC design flaws
87 static void
88 delay(void)
90 inb(0x84);
91 inb(0x84);
92 inb(0x84);
93 inb(0x84);
96 static void
97 lpt_putc(int c)
99 int i;
101 for (i = 0; !(inb(0x378+1) & 0x80) && i < 12800; i++)
102 delay();
103 outb(0x378+0, c);
104 outb(0x378+2, 0x08|0x04|0x01);
105 outb(0x378+2, 0x08);
111 /***** Text-mode CGA/VGA display output *****/
113 static unsigned addr_6845;
114 static uint16_t *crt_buf;
115 static uint16_t crt_pos;
117 void
118 cga_init(void)
120 volatile uint16_t *cp;
121 uint16_t was;
122 unsigned pos;
124 cp = (uint16_t*) (KERNBASE + CGA_BUF);
125 was = *cp;
126 *cp = (uint16_t) 0xA55A;
127 if (*cp != 0xA55A) {
128 cp = (uint16_t*) (KERNBASE + MONO_BUF);
129 addr_6845 = MONO_BASE;
130 } else {
131 *cp = was;
132 addr_6845 = CGA_BASE;
135 /* Extract cursor location */
136 outb(addr_6845, 14);
137 pos = inb(addr_6845 + 1) << 8;
138 outb(addr_6845, 15);
139 pos |= inb(addr_6845 + 1);
141 crt_buf = (uint16_t*) cp;
142 crt_pos = pos;
147 void
148 cga_putc(int c)
150 // if no attribute given, then use black on white
151 if (!(c & ~0xFF))
152 c |= 0x0700;
154 switch (c & 0xff) {
155 case '\b':
156 if (crt_pos > 0) {
157 crt_pos--;
158 crt_buf[crt_pos] = (c & ~0xff) | ' ';
160 break;
161 case '\n':
162 crt_pos += CRT_COLS;
163 /* fallthru */
164 case '\r':
165 crt_pos -= (crt_pos % CRT_COLS);
166 break;
167 case '\t':
168 cons_putc(' ');
169 cons_putc(' ');
170 cons_putc(' ');
171 cons_putc(' ');
172 cons_putc(' ');
173 break;
174 default:
175 crt_buf[crt_pos++] = c; /* write the character */
176 break;
179 // What is the purpose of this?
180 if (crt_pos >= CRT_SIZE) {
181 int i;
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 | ' ';
186 crt_pos -= CRT_COLS;
189 /* move that little blinky thing */
190 outb(addr_6845, 14);
191 outb(addr_6845 + 1, crt_pos >> 8);
192 outb(addr_6845, 15);
193 outb(addr_6845 + 1, crt_pos);
197 /***** Keyboard input code *****/
199 #define NO 0
201 #define SHIFT (1<<0)
202 #define CTL (1<<1)
203 #define ALT (1<<2)
205 #define CAPSLOCK (1<<3)
206 #define NUMLOCK (1<<4)
207 #define SCROLLLOCK (1<<5)
209 #define E0ESC (1<<6)
211 static uint8_t shiftcode[256] =
213 [0x1D] CTL,
214 [0x2A] SHIFT,
215 [0x36] SHIFT,
216 [0x38] ALT,
217 [0x9D] CTL,
218 [0xB8] ALT
221 static uint8_t togglecode[256] =
223 [0x3A] CAPSLOCK,
224 [0x45] NUMLOCK,
225 [0x46] SCROLLLOCK
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,
281 [0x97] KEY_HOME,
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] = {
290 normalmap,
291 shiftmap,
292 ctlmap,
293 ctlmap
297 * Get data from the keyboard. If we finish a character, return it. Else 0.
298 * Return -1 if no data.
300 static int
301 kbd_proc_data(void)
303 int c;
304 uint8_t data;
305 static uint32_t shift;
307 if ((inb(KBSTATP) & KBS_DIB) == 0)
308 return -1;
310 data = inb(KBDATAP);
312 if (data == 0xE0) {
313 // E0 escape character
314 shift |= E0ESC;
315 return 0;
316 } else if (data & 0x80) {
317 // Key released
318 data = (shift & E0ESC ? data : data & 0x7F);
319 shift &= ~(shiftcode[data] | E0ESC);
320 return 0;
321 } else if (shift & E0ESC) {
322 // Last character was an E0 escape; or with 0x80
323 data |= 0x80;
324 shift &= ~E0ESC;
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')
333 c += 'A' - 'a';
334 else if ('A' <= c && c <= 'Z')
335 c += 'a' - 'A';
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
345 return c;
348 void
349 kbd_intr(void)
351 cons_intr(kbd_proc_data);
354 void
355 kbd_init(void)
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
368 static struct {
369 uint8_t buf[CONSBUFSIZE];
370 uint32_t rpos;
371 uint32_t wpos;
372 } cons;
374 // called by device interrupt routines to feed input characters
375 // into the circular console input buffer.
376 void
377 cons_intr(int (*proc)(void))
379 int c;
381 while ((c = (*proc)()) != -1) {
382 if (c == 0)
383 continue;
384 cons.buf[cons.wpos++] = c;
385 if (cons.wpos == CONSBUFSIZE)
386 cons.wpos = 0;
390 // return the next input character from the console, or 0 if none waiting
392 cons_getc(void)
394 int c;
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).
399 serial_intr();
400 kbd_intr();
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)
406 cons.rpos = 0;
407 return c;
409 return 0;
412 // output a character to the console
413 void
414 cons_putc(int c)
416 lpt_putc(c);
417 cga_putc(c);
420 // initialize the console devices
421 void
422 cons_init(void)
424 cga_init();
425 kbd_init();
426 serial_init();
428 if (!serial_exists)
429 cprintf("Serial port does not exist!\n");
433 // `High'-level console I/O. Used by readline and cprintf.
435 void
436 cputchar(int c)
438 cons_putc(c);
442 getchar(void)
444 int c;
446 while ((c = cons_getc()) == 0)
447 /* do nothing */;
448 return c;
452 iscons(int fdnum)
454 // used by readline
455 return 1;