xref: /xv6-public/console.c (revision 29d8c2ee)
1 // Console input and output.
2 // Input is from the keyboard or serial port.
3 // Output is written to the screen and serial port.
4 
5 #include "types.h"
6 #include "defs.h"
7 #include "param.h"
8 #include "traps.h"
9 #include "spinlock.h"
10 #include "fs.h"
11 #include "file.h"
12 #include "mmu.h"
13 #include "proc.h"
14 #include "x86.h"
15 
16 static void consputc(int);
17 
18 static int panicked = 0;
19 
20 static struct {
21 	struct spinlock lock;
22 	int locking;
23 } cons;
24 
25 static void
26 printint(int xx, int base, int sgn)
27 {
28   static char digits[] = "0123456789abcdef";
29   char buf[16];
30   int i = 0, neg = 0;
31   uint x;
32 
33   if(sgn && xx < 0){
34     neg = 1;
35     x = -xx;
36   } else
37     x = xx;
38 
39   do{
40     buf[i++] = digits[x % base];
41   }while((x /= base) != 0);
42   if(neg)
43     buf[i++] = '-';
44 
45   while(--i >= 0)
46     consputc(buf[i]);
47 }
48 
49 //PAGEBREAK: 50
50 // Print to the console. only understands %d, %x, %p, %s.
51 void
52 cprintf(char *fmt, ...)
53 {
54   int i, c, state, locking;
55   uint *argp;
56   char *s;
57 
58   locking = cons.locking;
59   if(locking)
60     acquire(&cons.lock);
61 
62   argp = (uint*)(void*)(&fmt + 1);
63   state = 0;
64   for(i = 0; (c = fmt[i] & 0xff) != 0; i++){
65     if(c != '%'){
66       consputc(c);
67       continue;
68     }
69     c = fmt[++i] & 0xff;
70     if(c == 0)
71       break;
72     switch(c){
73     case 'd':
74       printint(*argp++, 10, 1);
75       break;
76     case 'x':
77     case 'p':
78       printint(*argp++, 16, 0);
79       break;
80     case 's':
81       if((s = (char*)*argp++) == 0)
82         s = "(null)";
83       for(; *s; s++)
84         consputc(*s);
85       break;
86     case '%':
87       consputc('%');
88       break;
89     default:
90       // Print unknown % sequence to draw attention.
91       consputc('%');
92       consputc(c);
93       break;
94     }
95   }
96 
97   if(locking)
98     release(&cons.lock);
99 }
100 
101 void
102 panic(char *s)
103 {
104   int i;
105   uint pcs[10];
106 
107   cli();
108   cons.locking = 0;
109   cprintf("cpu%d: panic: ", cpu->id);
110   cprintf(s);
111   cprintf("\n");
112   getcallerpcs(&s, pcs);
113   for(i=0; i<10; i++)
114     cprintf(" %p", pcs[i]);
115   panicked = 1; // freeze other CPU
116   for(;;)
117     ;
118 }
119 
120 //PAGEBREAK: 50
121 #define BACKSPACE 0x100
122 #define CRTPORT 0x3d4
123 static ushort *crt = (ushort*)0xb8000;  // CGA memory
124 
125 static void
126 cgaputc(int c)
127 {
128   int pos;
129 
130   // Cursor position: col + 80*row.
131   outb(CRTPORT, 14);
132   pos = inb(CRTPORT+1) << 8;
133   outb(CRTPORT, 15);
134   pos |= inb(CRTPORT+1);
135 
136   if(c == '\n')
137     pos += 80 - pos%80;
138   else if(c == BACKSPACE){
139     if(pos > 0)
140       crt[--pos] = ' ' | 0x0700;
141   } else
142     crt[pos++] = (c&0xff) | 0x0700;  // black on white
143 
144   if((pos/80) >= 24){  // Scroll up.
145     memmove(crt, crt+80, sizeof(crt[0])*23*80);
146     pos -= 80;
147     memset(crt+pos, 0, sizeof(crt[0])*(24*80 - pos));
148   }
149 
150   outb(CRTPORT, 14);
151   outb(CRTPORT+1, pos>>8);
152   outb(CRTPORT, 15);
153   outb(CRTPORT+1, pos);
154   crt[pos] = ' ' | 0x0700;
155 }
156 
157 void
158 consputc(int c)
159 {
160   if(panicked){
161     cli();
162     for(;;)
163       ;
164   }
165 
166   if (c == BACKSPACE) {
167     uartputc('\b');
168     uartputc(' ');
169     uartputc('\b');
170   } else
171     uartputc(c);
172   cgaputc(c);
173 }
174 
175 //PAGEBREAK: 50
176 #define INPUT_BUF 128
177 struct {
178   struct spinlock lock;
179   char buf[INPUT_BUF];
180   uint r;  // Read index
181   uint w;  // Write index
182   uint e;  // Edit index
183 } input;
184 
185 #define C(x)  ((x)-'@')  // Control-x
186 
187 void
188 consoleintr(int (*getc)(void))
189 {
190   int c;
191 
192   acquire(&input.lock);
193   while((c = getc()) >= 0){
194     switch(c){
195     case C('P'):  // Process listing.
196       procdump();
197       break;
198     case C('U'):  // Kill line.
199       while(input.e != input.w &&
200             input.buf[(input.e-1) % INPUT_BUF] != '\n'){
201         input.e--;
202         consputc(BACKSPACE);
203       }
204       break;
205     case C('H'):  // Backspace
206     case '\x7f':
207       if(input.e != input.w){
208         input.e--;
209         consputc(BACKSPACE);
210       }
211       break;
212     default:
213       if(c != 0 && input.e-input.r < INPUT_BUF){
214         // The serial port produces 0x13, not 0x10
215         if(c == '\r')
216           c = '\n';
217         input.buf[input.e++ % INPUT_BUF] = c;
218         consputc(c);
219         if(c == '\n' || c == C('D') || input.e == input.r+INPUT_BUF){
220           input.w = input.e;
221           wakeup(&input.r);
222         }
223       }
224       break;
225     }
226   }
227   release(&input.lock);
228 }
229 
230 int
231 consoleread(struct inode *ip, char *dst, int n)
232 {
233   uint target;
234   int c;
235 
236   iunlock(ip);
237   target = n;
238   acquire(&input.lock);
239   while(n > 0){
240     while(input.r == input.w){
241       if(proc->killed){
242         release(&input.lock);
243         ilock(ip);
244         return -1;
245       }
246       sleep(&input.r, &input.lock);
247     }
248     c = input.buf[input.r++ % INPUT_BUF];
249     if(c == C('D')){  // EOF
250       if(n < target){
251         // Save ^D for next time, to make sure
252         // caller gets a 0-byte result.
253         input.r--;
254       }
255       break;
256     }
257     *dst++ = c;
258     --n;
259     if(c == '\n')
260       break;
261   }
262   release(&input.lock);
263   ilock(ip);
264 
265   return target - n;
266 }
267 
268 int
269 consolewrite(struct inode *ip, char *buf, int n)
270 {
271   int i;
272 
273   iunlock(ip);
274   acquire(&cons.lock);
275   for(i = 0; i < n; i++)
276     consputc(buf[i] & 0xff);
277   release(&cons.lock);
278   ilock(ip);
279 
280   return n;
281 }
282 
283 void
284 consoleinit(void)
285 {
286   initlock(&cons.lock, "console");
287   initlock(&input.lock, "input");
288 
289   devsw[CONSOLE].write = consolewrite;
290   devsw[CONSOLE].read = consoleread;
291   cons.locking = 1;
292 
293   picenable(IRQ_KBD);
294   ioapicenable(IRQ_KBD, 0);
295 }
296 
297