
Added hotkeys to switch between console buffers and to return to the home one Added clear screen command Added debug text outputs to the hello command and when loading sh
511 lines
12 KiB
C
511 lines
12 KiB
C
// 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 {
|
|
ushort screenbuffer[SCRWIDTH * SCRHEIGHT];
|
|
struct kbdbuffer keybuffer;
|
|
int pos;
|
|
int active;
|
|
};
|
|
|
|
struct vconsole consoles[MAXVCONSOLES];
|
|
static uint currentconsoleindex = 0;
|
|
|
|
#define C(x) ((x) - '@') // Control-x
|
|
|
|
void clearconsole(ushort *bufferin);
|
|
void loadscreenbuffer(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 {
|
|
currentbuffer[pos++] = (c & 0xff) | 0x0700; // black on white
|
|
|
|
}
|
|
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));
|
|
}
|
|
consoles[consoleindex].pos = pos;
|
|
|
|
if (consoleindex == currentconsoleindex)
|
|
{
|
|
loadscreenbuffer(currentbuffer);
|
|
|
|
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, 0x0700, 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 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 pos = TITLEOFF;
|
|
|
|
//testfillbuffer(consoles[currentconsoleindex].screenbuffer);
|
|
clearconsole(consoles[currentconsoleindex].screenbuffer);
|
|
loadscreenbuffer(consoles[currentconsoleindex].screenbuffer);
|
|
|
|
consoles[currentconsoleindex].pos = pos;
|
|
|
|
outb(CRTPORT, 14);
|
|
outb(CRTPORT + 1, pos >> 8);
|
|
outb(CRTPORT, 15);
|
|
outb(CRTPORT + 1, pos);
|
|
}
|
|
|
|
void consoleinit(void) {
|
|
initlock(&cons.lock, "console");
|
|
|
|
consoles[currentconsoleindex].active = 1;
|
|
|
|
// Initialise pointer to point to our console input buffer
|
|
input = &consoles[currentconsoleindex].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)
|
|
{
|
|
acquire(&cons.lock);
|
|
currentconsoleindex = consoleindex;
|
|
input = &consoles[currentconsoleindex].keybuffer;
|
|
//ioapicenable(IRQ_KBD, 0);
|
|
release(&cons.lock);
|
|
|
|
loadscreenbuffer(consoles[currentconsoleindex].screenbuffer);
|
|
|
|
if (!consoles[currentconsoleindex].active)
|
|
{
|
|
clearscreen();
|
|
cprintf("Welcome to Console: %d\n", currentconsoleindex);
|
|
consoles[currentconsoleindex].active = 1;
|
|
}
|
|
else
|
|
{
|
|
int pos = consoles[currentconsoleindex].pos;
|
|
|
|
outb(CRTPORT, 14);
|
|
outb(CRTPORT + 1, pos >> 8);
|
|
outb(CRTPORT, 15);
|
|
outb(CRTPORT + 1, pos);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int closeconsole(int consoleindex)
|
|
{
|
|
clearconsole(consoles[currentconsoleindex].screenbuffer);
|
|
consoles[currentconsoleindex].active = 0;
|
|
switchtoconsole(0);
|
|
return 0;
|
|
}
|
|
|
|
int getcurrentconsoleindex(void)
|
|
{
|
|
return currentconsoleindex;
|
|
} |