Added scrolling console
[akaros.git] / kern / 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         outb(COM1+COM_DLL, (uint8_t) (115200 / 9600));
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         if(crt_scrolled) {
433                 crt_scrolled = FALSE;
434                 reset_screen();
435         }
436
437         //Force character to capital if caps lock on
438         if (shift & CAPSLOCK) {
439                 if ('a' <= c && c <= 'z')
440                         c += 'A' - 'a';
441                 else if ('A' <= c && c <= 'Z')
442                         c += 'a' - 'A';
443         }
444
445         // Process special keys
446         // Ctrl-Alt-Del: reboot
447         if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
448                 cprintf("Rebooting!\n");
449                 outb(0x92, 0x3); // courtesy of Chris Frost
450         }
451
452         return c;
453 }
454
455 void
456 kbd_intr(void)
457 {
458         cons_intr(kbd_proc_data);
459 }
460
461 void
462 kbd_init(void)
463 {
464 }
465
466
467
468 /***** General device-independent console code *****/
469 // Here we manage the console input buffer,
470 // where we stash characters received from the keyboard or serial port
471 // whenever the corresponding interrupt occurs.
472
473 #define CONSBUFSIZE     512
474
475 static struct {
476         uint8_t buf[CONSBUFSIZE];
477         uint32_t rpos;
478         uint32_t wpos;
479 } cons;
480
481 // called by device interrupt routines to feed input characters
482 // into the circular console input buffer.
483 void
484 cons_intr(int (*proc)(void))
485 {
486         int c;
487
488         while ((c = (*proc)()) != -1) {
489                 if (c == 0)
490                         continue;
491                 cons.buf[cons.wpos++] = c;
492                 if (cons.wpos == CONSBUFSIZE)
493                         cons.wpos = 0;
494         }
495 }
496
497 // return the next input character from the console, or 0 if none waiting
498 int
499 cons_getc(void)
500 {
501         int c;
502
503         // poll for any pending input characters,
504         // so that this function works even when interrupts are disabled
505         // (e.g., when called from the kernel monitor).
506         serial_intr();
507         kbd_intr();
508
509         // grab the next character from the input buffer.
510         if (cons.rpos != cons.wpos) {
511                 c = cons.buf[cons.rpos++];
512                 if (cons.rpos == CONSBUFSIZE)
513                         cons.rpos = 0;
514                 return c;
515         }
516         return 0;
517 }
518
519 // output a character to the console
520 void
521 cons_putc(int c)
522 {
523         serial_putc(c);
524         //lpt_putc(c);
525         cga_putc(c);
526 }
527
528 // initialize the console devices
529 void
530 cons_init(void)
531 {
532         cga_init();
533         kbd_init();
534         serial_init();
535
536         if (!serial_exists)
537                 cprintf("Serial port does not exist!\n");
538 }
539
540
541 // `High'-level console I/O.  Used by readline and cprintf.
542
543 void
544 cputchar(int c)
545 {
546         cons_putc(c);
547 }
548
549 int
550 getchar(void)
551 {
552         int c;
553
554         while ((c = cons_getc()) == 0)
555                 /* do nothing */;
556         return c;
557 }
558
559 int
560 iscons(int fdnum)
561 {
562         // used by readline
563         return 1;
564 }