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