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