Minor touchups after rebase to make it work
[akaros.git] / kern / src / console.c
1 /* See COPYRIGHT for copyright information. */
2
3 #include <arch/x86.h>
4 #include <arch/console.h>
5 #include <kbdreg.h>
6 #include <string.h>
7 #include <assert.h>
8
9 #include <ros/memlayout.h>
10
11 void cons_intr(int (*proc)(void));
12 void scroll_screen(void);
13
14
15 /***** Serial I/O code *****/
16
17 #define COM1            0x3F8
18
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
35 #define COM_LSR_READY   0x20    //   Ready to send
36
37 static bool serial_exists;
38
39 int
40 serial_proc_data(void)
41 {
42         if (!(inb(COM1+COM_LSR) & COM_LSR_DATA))
43                 return -1;
44         return inb(COM1+COM_RX);
45 }
46
47 void
48 serial_intr(void)
49 {
50         if (serial_exists)
51                 cons_intr(serial_proc_data);
52 }
53
54 void
55 serial_init(void)
56 {
57         // Turn off the FIFO
58         outb(COM1+COM_FCR, 0);
59         
60         // Set speed; requires DLAB latch
61         outb(COM1+COM_LCR, COM_LCR_DLAB);
62         // Setting speed to 115200 (setting the divider to 1)
63         outb(COM1+COM_DLL, 1);
64         outb(COM1+COM_DLM, 0);
65
66         // 8 data bits, 1 stop bit, parity off; turn off DLAB latch
67         outb(COM1+COM_LCR, COM_LCR_WLEN8 & ~COM_LCR_DLAB);
68
69         // No modem controls
70         outb(COM1+COM_MCR, 0);
71         // Enable rcv interrupts
72         outb(COM1+COM_IER, COM_IER_RDI);
73
74         // Clear any preexisting overrun indications and interrupts
75         // Serial port doesn't exist if COM_LSR returns 0xFF
76         serial_exists = (inb(COM1+COM_LSR) != 0xFF);
77         (void) inb(COM1+COM_IIR);
78         (void) inb(COM1+COM_RX);
79
80 }
81
82 static void
83 serial_send_byte(uint8_t b)
84 {
85         while (!(inb(COM1+COM_LSR) & COM_LSR_READY));
86         outb(COM1, b);
87 }
88
89 static void
90 serial_putc(int c)
91 {
92         switch (c & 0xff) {
93         case '\b':
94                 serial_send_byte('\b');
95                 serial_send_byte((uint8_t)(' '));
96                 serial_send_byte('\b');
97                 break;
98         case '\n':
99         case '\r':
100                 serial_send_byte((uint8_t)('\n'));
101                 serial_send_byte((uint8_t)('\r'));
102                 break;
103         default:
104                 serial_send_byte((uint8_t)(c & 0xff));
105                 break;
106         }
107         return;
108 }
109
110
111
112 /***** Parallel port output code *****/
113 // For information on PC parallel port programming, see the class References
114 // page.
115
116 // Stupid I/O delay routine necessitated by historical PC design flaws
117 static void
118 delay(void)
119 {
120         inb(0x84);
121         inb(0x84);
122         inb(0x84);
123         inb(0x84);
124 }
125
126 static void
127 lpt_putc(int c)
128 {
129         int i;
130
131         for (i = 0; !(inb(0x378+1) & 0x80) && i < 12800; i++)
132                 delay();
133         outb(0x378+0, c);
134         outb(0x378+2, 0x08|0x04|0x01);
135         outb(0x378+2, 0x08);
136 }
137
138
139
140
141 /***** Text-mode CGA/VGA display output with scrolling *****/
142 #define MAX_SCROLL_LENGTH       20
143 #define SCROLLING_CRT_SIZE      (MAX_SCROLL_LENGTH * CRT_SIZE)
144
145 static unsigned addr_6845;
146 static uint16_t *COUNT(CRT_SIZE) crt_buf;
147 static uint16_t crt_pos;
148
149 static uint16_t scrolling_crt_buf[SCROLLING_CRT_SIZE];
150 static uint16_t scrolling_crt_pos;
151 static uint8_t  current_crt_buf;
152
153 void
154 cga_init(void)
155 {
156         volatile uint16_t *COUNT(CRT_SIZE) cp;
157         uint16_t was;
158         unsigned pos;
159
160         cp = (uint16_t *COUNT(CRT_SIZE)) TC(KERNBASE + CGA_BUF);
161         was = *cp;
162         *cp = (uint16_t) 0xA55A;
163         if (*cp != 0xA55A) {
164                 cp = (uint16_t *COUNT(CRT_SIZE)) TC(KERNBASE + MONO_BUF);
165                 addr_6845 = MONO_BASE;
166         } else {
167                 *cp = was;
168                 addr_6845 = CGA_BASE;
169         }
170         
171         /* Extract cursor location */
172         outb(addr_6845, 14);
173         pos = inb(addr_6845 + 1) << 8;
174         outb(addr_6845, 15);
175         pos |= inb(addr_6845 + 1);
176
177         crt_buf = (uint16_t *COUNT(CRT_SIZE)) cp;
178         crt_pos = pos;
179         scrolling_crt_pos = 0;
180         current_crt_buf = 0;
181 }
182
183 static void set_screen(uint8_t screen_num) {
184         uint16_t leftovers = (scrolling_crt_pos % CRT_COLS);
185         leftovers = (leftovers) ? CRT_COLS - leftovers : 0;
186         
187         int offset = scrolling_crt_pos + leftovers - (screen_num + 1)*CRT_SIZE;
188         offset = (offset > 0) ? offset : 0;
189
190         memcpy(crt_buf, scrolling_crt_buf + offset, CRT_SIZE * sizeof(uint16_t));
191 }
192
193 static void scroll_screen_up(void) {
194         if(current_crt_buf <  (scrolling_crt_pos / CRT_SIZE))
195                 current_crt_buf++;
196         set_screen(current_crt_buf);
197 }
198
199 static void scroll_screen_down(void) {
200         if(current_crt_buf > 0) 
201                 current_crt_buf--;
202         set_screen(current_crt_buf);
203 }
204
205 static void reset_screen(void) {
206         current_crt_buf = 0;
207         set_screen(current_crt_buf);
208 }
209
210 void
211 cga_putc(int c)
212 {
213         // if no attribute given, then use black on white
214         if (!(c & ~0xFF))
215                 c |= 0x0700;
216
217         switch (c & 0xff) {
218         case '\b':
219                 if (crt_pos > 0) {
220                         crt_pos--;
221                         scrolling_crt_pos--;
222
223                         crt_buf[crt_pos] = (c & ~0xff) | ' ';
224                         scrolling_crt_buf[scrolling_crt_pos] = crt_buf[crt_pos];
225                 }
226                 break;
227         case '\n':
228                 crt_pos += CRT_COLS;
229                 scrolling_crt_pos += CRT_COLS;
230                 /* fallthru */
231         case '\r':
232                 crt_pos -= (crt_pos % CRT_COLS);
233                 scrolling_crt_pos -= (scrolling_crt_pos % CRT_COLS);
234                 break;
235         case '\t':
236                 cons_putc(' ');
237                 cons_putc(' ');
238                 cons_putc(' ');
239                 cons_putc(' ');
240                 cons_putc(' ');
241                 break;
242         default:
243                 crt_buf[crt_pos++] = c;         /* write the character */
244                 scrolling_crt_buf[scrolling_crt_pos++] = c;
245                 break;
246         }
247
248         // The purpose of this is to allow the screen to appear as if it is scrolling as
249         // more lines are added beyond the size of the monitor.  The top line is dropped
250         // and everything is shifted up by one.
251         if (crt_pos >= CRT_SIZE) {
252                 int i;
253
254                 memcpy(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
255                 for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++)
256                         crt_buf[i] = 0x0700 | ' ';
257                 crt_pos -= CRT_COLS;
258         }
259         // Do the same for the scrolling crt buffer when it hits its capacity
260         if (scrolling_crt_pos >= SCROLLING_CRT_SIZE) {
261                 int i;
262
263                 memcpy(scrolling_crt_buf, scrolling_crt_buf + CRT_COLS, 
264                        (SCROLLING_CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
265                 for (i = SCROLLING_CRT_SIZE - CRT_COLS; i < SCROLLING_CRT_SIZE; i++)
266                         scrolling_crt_buf[i] = 0x0700 | ' ';
267                 scrolling_crt_pos -= CRT_COLS;
268         }
269
270
271         /* move that little blinky thing */
272         outb(addr_6845, 14);
273         outb(addr_6845 + 1, crt_pos >> 8);
274         outb(addr_6845, 15);
275         outb(addr_6845 + 1, crt_pos);
276 }
277
278
279 /***** Keyboard input code *****/
280
281 #define NO              0
282
283 #define SHIFT   (1<<0)
284 #define CTL             (1<<1)
285 #define ALT             (1<<2)
286
287 #define CAPSLOCK        (1<<3)
288 #define NUMLOCK         (1<<4)
289 #define SCROLLLOCK      (1<<5)
290
291 #define E0ESC           (1<<6)
292
293 static uint8_t shiftcode[256] = 
294 {
295         [0x1D] CTL,
296         [0x2A] SHIFT,
297         [0x36] SHIFT,
298         [0x38] ALT,
299         [0x9D] CTL,
300         [0xB8] ALT
301 };
302
303 static uint8_t togglecode[256] = 
304 {
305         [0x3A] CAPSLOCK,
306         [0x45] NUMLOCK,
307         [0x46] SCROLLLOCK
308 };
309
310 static uint8_t normalmap[256] =
311 {
312         NO,   0x1B, '1',  '2',  '3',  '4',  '5',  '6',  // 0x00
313         '7',  '8',  '9',  '0',  '-',  '=',  '\b', '\t',
314         'q',  'w',  'e',  'r',  't',  'y',  'u',  'i',  // 0x10
315         'o',  'p',  '[',  ']',  '\n', NO,   'a',  's',
316         'd',  'f',  'g',  'h',  'j',  'k',  'l',  ';',  // 0x20
317         '\'', '`',  NO,   '\\', 'z',  'x',  'c',  'v',
318         'b',  'n',  'm',  ',',  '.',  '/',  NO,   '*',  // 0x30
319         NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO,
320         NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',  // 0x40
321         '8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',
322         '2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,   // 0x50
323         [0xC7] KEY_HOME,        [0x9C] '\n' /*KP_Enter*/,
324         [0xB5] '/' /*KP_Div*/,  [0xC8] KEY_UP,
325         [0xC9] KEY_PGUP,        [0xCB] KEY_LF,
326         [0xCD] KEY_RT,          [0xCF] KEY_END,
327         [0xD0] KEY_DN,          [0xD1] KEY_PGDN,
328         [0xD2] KEY_INS,         [0xD3] KEY_DEL
329 };
330
331 static uint8_t shiftmap[256] = 
332 {
333         NO,   033,  '!',  '@',  '#',  '$',  '%',  '^',  // 0x00
334         '&',  '*',  '(',  ')',  '_',  '+',  '\b', '\t',
335         'Q',  'W',  'E',  'R',  'T',  'Y',  'U',  'I',  // 0x10
336         'O',  'P',  '{',  '}',  '\n', NO,   'A',  'S',
337         'D',  'F',  'G',  'H',  'J',  'K',  'L',  ':',  // 0x20
338         '"',  '~',  NO,   '|',  'Z',  'X',  'C',  'V',
339         'B',  'N',  'M',  '<',  '>',  '?',  NO,   '*',  // 0x30
340         NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO,
341         NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',  // 0x40
342         '8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',
343         '2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,   // 0x50
344         [0xC7] KEY_HOME,        [0x9C] '\n' /*KP_Enter*/,
345         [0xB5] '/' /*KP_Div*/,  [0xC8] KEY_UP,
346         [0xC9] KEY_PGUP,        [0xCB] KEY_LF,
347         [0xCD] KEY_RT,          [0xCF] KEY_END,
348         [0xD0] KEY_DN,          [0xD1] KEY_PGDN,
349         [0xD2] KEY_INS,         [0xD3] KEY_DEL
350 };
351
352 #define C(x) (x - '@')
353
354 static uint8_t ctlmap[256] = 
355 {
356         NO,      NO,      NO,      NO,      NO,      NO,      NO,      NO, 
357         NO,      NO,      NO,      NO,      NO,      NO,      NO,      NO, 
358         C('Q'),  C('W'),  C('E'),  C('R'),  C('T'),  C('Y'),  C('U'),  C('I'),
359         C('O'),  C('P'),  NO,      NO,      '\r',    NO,      C('A'),  C('S'),
360         C('D'),  C('F'),  C('G'),  C('H'),  C('J'),  C('K'),  C('L'),  NO, 
361         NO,      NO,      NO,      C('\\'), C('Z'),  C('X'),  C('C'),  C('V'),
362         C('B'),  C('N'),  C('M'),  NO,      NO,      C('/'),  NO,      NO,
363         [0x97] KEY_HOME,
364         [0xB5] C('/'),          [0xC8] KEY_UP,
365         [0xC9] KEY_PGUP,        [0xCB] KEY_LF,
366         [0xCD] KEY_RT,          [0xCF] KEY_END,
367         [0xD0] KEY_DN,          [0xD1] KEY_PGDN,
368         [0xD2] KEY_INS,         [0xD3] KEY_DEL
369 };
370
371 static uint8_t * COUNT(256) charcode[4] = {
372         normalmap,
373         shiftmap,
374         ctlmap,
375         ctlmap
376 };
377
378 /*
379  * Get data from the keyboard.  If we finish a character, return it.  Else 0.
380  * Return -1 if no data.
381  */
382 static int
383 kbd_proc_data(void)
384 {
385         int c;
386         uint8_t data;
387         static uint32_t shift;
388         static bool crt_scrolled = FALSE;
389
390         if ((inb(KBSTATP) & KBS_DIB) == 0)
391                 return -1;
392
393         data = inb(KBDATAP);
394
395         if (data == 0xE0) {
396                 // E0 escape character
397                 shift |= E0ESC;
398                 return 0;
399         } else if (data & 0x80) {
400                 // Key released
401                 data = (shift & E0ESC ? data : data & 0x7F);
402                 shift &= ~(shiftcode[data] | E0ESC);
403                 return 0;
404         } else if (shift & E0ESC) {
405                 // Last character was an E0 escape; or with 0x80
406                 data |= 0x80;
407                 shift &= ~E0ESC;
408         }
409
410         shift |= shiftcode[data];
411         shift ^= togglecode[data];
412
413         c = charcode[shift & (CTL | SHIFT)][data];
414
415         //Scrolling screen functionality
416         if((shift & SHIFT) && ((c == KEY_UP) || (c == KEY_PGUP))) {
417                 crt_scrolled = TRUE;
418                 scroll_screen_up();
419                 return 0;
420         }
421         else if((shift & SHIFT) && ((c == KEY_DN) || (c == KEY_PGDN))) {
422                 crt_scrolled = TRUE;
423                 scroll_screen_down();
424                 return 0;
425         }
426         else if((shift & SHIFT) && c == KEY_RT) {
427                 crt_scrolled = FALSE;
428                 reset_screen();
429                 return 0;
430         }
431
432         // On keypress other than SHIFT, reset if we were scrolled
433         if(crt_scrolled && (!(shift & SHIFT))) {
434                 crt_scrolled = FALSE;
435                 reset_screen();
436         }
437
438         //Force character to capital if caps lock on
439         if (shift & CAPSLOCK) {
440                 if ('a' <= c && c <= 'z')
441                         c += 'A' - 'a';
442                 else if ('A' <= c && c <= 'Z')
443                         c += 'a' - 'A';
444         }
445
446         // Process special keys
447         // Ctrl-Alt-Del: reboot
448         if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
449                 cprintf("Rebooting!\n");
450                 outb(0x92, 0x3); // courtesy of Chris Frost
451         }
452
453         return c;
454 }
455
456 void
457 kbd_intr(void)
458 {
459         cons_intr(kbd_proc_data);
460 }
461
462 void
463 kbd_init(void)
464 {
465 }
466
467
468
469 /***** General device-independent console code *****/
470 // Here we manage the console input buffer,
471 // where we stash characters received from the keyboard or serial port
472 // whenever the corresponding interrupt occurs.
473
474 #define CONSBUFSIZE     512
475
476 static struct {
477         uint8_t buf[CONSBUFSIZE];
478         uint32_t rpos;
479         uint32_t wpos;
480 } cons;
481
482 // called by device interrupt routines to feed input characters
483 // into the circular console input buffer.
484 void
485 cons_intr(int (*proc)(void))
486 {
487         int c;
488
489         while ((c = (*proc)()) != -1) {
490                 if (c == 0)
491                         continue;
492                 cons.buf[cons.wpos++] = c;
493                 if (cons.wpos == CONSBUFSIZE)
494                         cons.wpos = 0;
495         }
496 }
497
498 // return the next input character from the console, or 0 if none waiting
499 int
500 cons_getc(void)
501 {
502         int c;
503
504         // poll for any pending input characters,
505         // so that this function works even when interrupts are disabled
506         // (e.g., when called from the kernel monitor).
507         serial_intr();
508         kbd_intr();
509
510         // grab the next character from the input buffer.
511         if (cons.rpos != cons.wpos) {
512                 c = cons.buf[cons.rpos++];
513                 if (cons.rpos == CONSBUFSIZE)
514                         cons.rpos = 0;
515                 return c;
516         }
517         return 0;
518 }
519
520 // output a character to the console
521 void
522 cons_putc(int c)
523 {
524         serial_putc(c);
525         //lpt_putc(c);
526         cga_putc(c);
527 }
528
529 // initialize the console devices
530 void
531 cons_init(void)
532 {
533         cga_init();
534         kbd_init();
535         serial_init();
536
537         if (!serial_exists)
538                 cprintf("Serial port does not exist!\n");
539 }
540
541
542 // `High'-level console I/O.  Used by readline and cprintf.
543
544 void
545 cputchar(int c)
546 {
547         cons_putc(c);
548 }
549
550 int
551 getchar(void)
552 {
553         int c;
554
555         while ((c = cons_getc()) == 0)
556                 /* do nothing */;
557         return c;
558 }
559
560 int
561 iscons(int fdnum)
562 {
563         // used by readline
564         return 1;
565 }