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