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