// Console input and output. // Input is from the keyboard or serial port. // Output is written to the screen and serial port. #include "types.h" #include "defs.h" #include "param.h" #include "traps.h" #include "spinlock.h" #include "sleeplock.h" #include "fs.h" #include "file.h" #include "memlayout.h" #include "mmu.h" #include "proc.h" #include "x86.h" #define INPUT_BUF 128 struct kbdbuffer { char buf[INPUT_BUF]; uint r; // Read index uint w; // Write index uint e; // Edit index }; struct kbdbuffer inputBuffer; struct kbdbuffer * input = 0; struct vconsole { struct spinlock lock; struct proc* processowner; ushort screenbuffer[SCRWIDTH * SCRHEIGHT]; struct kbdbuffer keybuffer; int pos; int active; }; struct vconsole consoles[MAXVCONSOLES]; struct vconsole* currentconsole = 0; static uint currentconsoleindex = 0; #define C(x) ((x) - '@') // Control-x void clearconsole(ushort *bufferin); void loadscreenbuffer(ushort *bufferin); void savescreenbuffer(ushort *bufferin); void clearscreen(void); static void consputc(int); static int panicked = 0; static struct { struct spinlock lock; int locking; } cons; static void printint(int xx, int base, int sign) { static char digits[] = "0123456789abcdef"; char buf[16]; int i; uint x; if (sign && (sign = xx < 0)) { x = -xx; } else { x = xx; } i = 0; do { buf[i++] = digits[x % base]; } while ((x /= base) != 0); if (sign) { buf[i++] = '-'; } while (--i >= 0) { consputc(buf[i]); } } // Print to the console. only understands %d, %x, %p, %s. void cprintf(char *fmt, ...) { int i, c, locking; uint *argp; char *s; locking = cons.locking; if (locking) { acquire(&cons.lock); } if (fmt == 0) { panic("null fmt"); } argp = (uint*)(void*)(&fmt + 1); for (i = 0; (c = fmt[i] & 0xff) != 0; i++) { if (c != '%') { consputc(c); continue; } c = fmt[++i] & 0xff; if (c == 0) { break; } switch (c) { case 'd': printint(*argp++, 10, 1); break; case 'x': case 'p': printint(*argp++, 16, 0); break; case 's': if ((s = (char*)*argp++) == 0) { s = "(null)"; } for (; *s; s++) { consputc(*s); } break; case '%': consputc('%'); break; default: // Print unknown % sequence to draw attention. consputc('%'); consputc(c); break; } } if (locking) { release(&cons.lock); } } void panic(char *s) { int i; uint pcs[10]; cli(); cons.locking = 0; // use lapiccpunum so that we can call panic from mycpu() cprintf("lapicid %d: panic: ", lapicid()); cprintf(s); cprintf("\n"); getcallerpcs(&s, pcs); for (i = 0; i < 10; i++) { cprintf(" %p", pcs[i]); } panicked = 1; // freeze other CPU for (;;) { ; } } #define BACKSPACE 0x100 #define CRTPORT 0x3d4 #define TITLEOFF (SCRWIDTH * 1) // Size of the offset we need for the title bar static ushort *crt = (ushort*)P2V(0xb8000); // CGA memory static void cgaputc(int c) { int pos; int consoleindex = 0; // Check if a process has actually been created otherwise use the base console if (myproc() != 0x0) { consoleindex = myproc()->consoleIndex; } ushort* currentbuffer = consoles[consoleindex].screenbuffer; if (consoleindex == currentconsoleindex) { // Cursor position: col + 80*row. outb(CRTPORT, 14); pos = inb(CRTPORT + 1) << 8; outb(CRTPORT, 15); pos |= inb(CRTPORT + 1); } else { pos = consoles[consoleindex].pos; } if (c == '\n') { pos += SCRWIDTH - pos % SCRWIDTH; } else if (c == BACKSPACE) { if (pos > (TITLEOFF)) { currentbuffer[pos] = 0; // Clear the character from the buffer --pos; } } else { //int posp = pos; if (consoleindex == currentconsoleindex) { crt[pos] = (c & 0xff) | 0x0700; // black on white } currentbuffer[pos] = (c & 0xff) | 0x0700; // black on white pos++; } if (pos < TITLEOFF || pos > SCRHEIGHT * SCRWIDTH) { panic("pos under/overflow"); } if ((pos / 80) >= 24) { // Scroll up. memmove(currentbuffer + TITLEOFF, currentbuffer + (SCRWIDTH + TITLEOFF), sizeof(crt[0]) * (SCRHEIGHT - 1) * SCRWIDTH); pos -= 80; memset(currentbuffer + pos, 0, sizeof(crt[0]) * (SCRHEIGHT * SCRWIDTH - pos)); if (consoleindex == currentconsoleindex) { memmove(crt + TITLEOFF, crt + (SCRWIDTH + TITLEOFF), sizeof(crt[0]) * (SCRHEIGHT - 1) * SCRWIDTH); memset(crt + pos, 0, sizeof(crt[0]) * (SCRHEIGHT * SCRWIDTH - pos)); } } consoles[consoleindex].pos = pos; if (consoleindex == currentconsoleindex) { outb(CRTPORT, 14); outb(CRTPORT + 1, pos >> 8); outb(CRTPORT, 15); outb(CRTPORT + 1, pos); crt[pos] = ' ' | 0x0700; } } void consputc(int c) { if (panicked) { cli(); for (;;) { ; } } if (c == BACKSPACE) { uartputc('\b'); uartputc(' '); uartputc('\b'); } else { uartputc(c); } cgaputc(c); } int consoleget(void) { int c; acquire(&cons.lock); while ((c = kbdgetc()) <= 0) { if (c == 0) { c = kbdgetc(); } } release(&cons.lock); return c; } void consoleintr(int (*getc)(void)) { int c, doprocdump = 0, doconsoleswitch = 0, doconsolehome = 0; acquire(&cons.lock); while ((c = getc()) >= 0) { switch (c) { case C('P'): // Process listing. // procdump() locks cons.lock indirectly; invoke later doprocdump = 1; break; case C('U'): // Kill line. while (input->e != input->w && input->buf[(input->e - 1) % INPUT_BUF] != '\n') { input->e--; consputc(BACKSPACE); } break; case C('H'): case '\x7f': // Backspace if (input->e != input->w) { input->e--; consputc(BACKSPACE); } break; case C('T'): doconsoleswitch = 1; break; case C('K'): doconsolehome = 1; break; default: if (c != 0 && input->e - input->r < INPUT_BUF) { c = (c == '\r') ? '\n' : c; input->buf[input->e++ % INPUT_BUF] = c; consputc(c); if (c == '\n' || c == C('D') || input->e == input->r + INPUT_BUF) { input->w = input->e; wakeup(&(input->r)); } } break; } } release(&cons.lock); if (doprocdump) { procdump(); // now call procdump() wo. cons.lock held } if (doconsolehome) { if (currentconsoleindex != 0) { switchtoconsole(0); } } if (doconsoleswitch) { int toconsole = 0; for (int i = (currentconsoleindex + 1); i < MAXVCONSOLES; i++) { if (consoles[i].active) { toconsole = i; break; } } if (toconsole != currentconsoleindex) { switchtoconsole(toconsole); } } } int consoleread(struct inode *ip, char *dst, int n) { uint target; int c; iunlock(ip); target = n; acquire(&cons.lock); while (n > 0) { while (input->r == input->w) { if (myproc()->killed) { release(&cons.lock); ilock(ip); return -1; } sleep(&(input->r), &cons.lock); } c = input->buf[input->r++ % INPUT_BUF]; if (c == C('D')) { // EOF if (n < target) { // Save ^D for next time, to make sure // caller gets a 0-byte result. input->r--; } break; } *dst++ = c; --n; if (c == '\n') { break; } } release(&cons.lock); ilock(ip); return target - n; } int consolewrite(struct inode *ip, char *buf, int n) { int i; iunlock(ip); acquire(&cons.lock); for (i = 0; i < n; i++) { consputc(buf[i] & 0xff); } release(&cons.lock); ilock(ip); return n; } void testfillbuffer(ushort *bufferin) { ushort firstchar = 65; ushort lastchar = 90; ushort currentchar = firstchar; for (int i = 0; i < (SCRHEIGHT * SCRWIDTH); i++) { if (currentchar > lastchar) { currentchar = firstchar; } bufferin[i] = (currentchar & 0xff) | 0x0700; currentchar++; } } void clearconsole(ushort *bufferin) { // Flood the screen buffer with blank spaces memset(bufferin, 0, sizeof(bufferin[0]) * SCRHEIGHT * SCRWIDTH); } void loadscreenbuffer(ushort *bufferin) { // Copy the memory from the console buffer to the crt buffer memmove(crt, bufferin, sizeof(bufferin[0]) * SCRHEIGHT * SCRWIDTH); } void savescreenbuffer(ushort *bufferin) { // Copy the memory from the console buffer to the crt buffer memmove(bufferin, crt, sizeof(crt[0]) * SCRHEIGHT * SCRWIDTH); } void clearscreen(void) { //cprintf("process id: %d", myproc()->consoleIndex); //return; //cprintf("size of buffer item: %d\n", sizeof(consoles[currentconsoleindex].screenbuffer[0]) * (SCRHEIGHT * SCRWIDTH)); //cprintf("size of crt item: %d\n", sizeof(crt[0]) * (SCRHEIGHT * SCRWIDTH)); //return; int consoleindex = 0; // Check if a process has actually been created otherwise use the base console if (myproc() != 0x0) { consoleindex = myproc()->consoleIndex; } if (consoleindex == currentconsoleindex) { acquire(&cons.lock); int pos = TITLEOFF; //testfillbuffer(consoles[currentconsoleindex].screenbuffer); clearconsole(crt); //loadscreenbuffer(consoles[currentconsoleindex].screenbuffer); release(&cons.lock); currentconsole->pos = pos; outb(CRTPORT, 14); outb(CRTPORT + 1, pos >> 8); outb(CRTPORT, 15); outb(CRTPORT + 1, pos); } else { } } void consoleinit(void) { initlock(&cons.lock, "console"); // Initialise pointer to point to our console input buffer currentconsole = &consoles[currentconsoleindex]; currentconsole->active = 1; initlock(¤tconsole->lock, "vconsole0"); input = ¤tconsole->keybuffer; devsw[CONSOLE].write = consolewrite; devsw[CONSOLE].read = consoleread; cons.locking = 1; clearscreen(); cprintf("Welcome! you are currently in the base console\n"); ioapicenable(IRQ_KBD, 0); } int newconsole(void) { int result = 0; for (int i = 1; i < MAXVCONSOLES; i++) { if (!consoles[i].active) { result = i; break; } } return result; } int switchtoconsole(int consoleindex) { int pos; /*outb(CRTPORT, 14); pos = inb(CRTPORT + 1) << 8; outb(CRTPORT, 15); pos |= inb(CRTPORT + 1);*/ //consoles[currentconsoleindex].pos = pos; //savescreenbuffer(consoles[currentconsoleindex].screenbuffer); //acquire(¤tconsole->lock); //sleep(&(currentconsole), ¤tconsole->lock); acquire(&cons.lock); currentconsoleindex = consoleindex; currentconsole = &consoles[currentconsoleindex]; input = ¤tconsole->keybuffer; //ioapicenable(IRQ_KBD, 0); loadscreenbuffer(currentconsole->screenbuffer); release(&cons.lock); if (!currentconsole->active) { clearscreen(); cprintf("Welcome to Console: %d\n", currentconsoleindex); currentconsole->active = 1; } else { pos = currentconsole->pos; outb(CRTPORT, 14); outb(CRTPORT + 1, pos >> 8); outb(CRTPORT, 15); outb(CRTPORT + 1, pos); } //wakeup(myproc()->chan); return 0; } int closeconsole(int consoleindex) { clearconsole(currentconsole->screenbuffer); currentconsole->active = 0; switchtoconsole(0); return 0; } int getcurrentconsoleindex(void) { return currentconsoleindex; }