1f0615dd5fe6a4ce098c4948790be3acb45d87df
[mw/milkymist.git] / software / demo / shell.c
1 /*
2  * Milkymist VJ SoC (Software)
3  * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3 of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <console.h>
22 #include <uart.h>
23 #include <cffat.h>
24 #include <system.h>
25 #include <math.h>
26 #include <irq.h>
27 #include <board.h>
28 #include <hw/pfpu.h>
29 #include <hw/tmu.h>
30 #include <hw/sysctl.h>
31 #include <hw/gpio.h>
32 #include <hw/interrupts.h>
33 #include <hw/minimac.h>
34
35 #include <hal/vga.h>
36 #include <hal/snd.h>
37 #include <hal/tmu.h>
38 #include <hal/time.h>
39 #include <hal/brd.h>
40
41 #include "line.h"
42 #include "wave.h"
43 #include "rpipe.h"
44 #include "cpustats.h"
45 #include "memstats.h"
46 #include "shell.h"
47 #include "renderer.h"
48
49 #define NUMBER_OF_BYTES_ON_A_LINE 16
50 static void dump_bytes(unsigned int *ptr, int count, unsigned addr)
51 {
52         char *data = (char *)ptr;
53         int line_bytes = 0, i = 0;
54
55         putsnonl("Memory dump:");
56         while(count > 0){
57                 line_bytes =
58                         (count > NUMBER_OF_BYTES_ON_A_LINE)?
59                                 NUMBER_OF_BYTES_ON_A_LINE : count;
60
61                 printf("\n0x%08x  ", addr);
62                 for(i=0;i<line_bytes;i++)
63                         printf("%02x ", *(unsigned char *)(data+i));
64
65                 for(;i<NUMBER_OF_BYTES_ON_A_LINE;i++)
66                         printf("   ");
67
68                 printf(" ");
69
70                 for(i=0;i<line_bytes;i++) {
71                         if((*(data+i) < 0x20) || (*(data+i) > 0x7e))
72                                 printf(".");
73                         else
74                                 printf("%c", *(data+i));
75                 }
76
77                 for(;i<NUMBER_OF_BYTES_ON_A_LINE;i++)
78                         printf(" ");
79
80                 data += (char)line_bytes;
81                 count -= line_bytes;
82                 addr += line_bytes;
83         }
84         printf("\n");
85 }
86
87 static void mr(char *startaddr, char *len)
88 {
89         char *c;
90         unsigned *addr;
91         unsigned length;
92
93         if(*startaddr == 0) {
94                 printf("mr <address> [length]\n");
95                 return;
96         }
97         addr = (unsigned *)strtoul(startaddr, &c, 0);
98         if(*c != 0) {
99                 printf("incorrect address\n");
100                 return;
101         }
102         if(*len == 0) {
103                 length = 1;
104         } else {
105                 length = strtoul(len, &c, 0);
106                 if(*c != 0) {
107                         printf("incorrect length\n");
108                         return;
109                 }
110         }
111
112         dump_bytes(addr, length, (unsigned)addr);
113 }
114
115 static void mw(char *addr, char *value, char *count)
116 {
117         char *c;
118         unsigned *addr2;
119         unsigned value2;
120         unsigned count2;
121         unsigned i;
122
123         if((*addr == 0) || (*value == 0)) {
124                 printf("mw <address> <value>\n");
125                 return;
126         }
127         addr2 = (unsigned *)strtoul(addr, &c, 0);
128         if(*c != 0) {
129                 printf("incorrect address\n");
130                 return;
131         }
132         value2 = strtoul(value, &c, 0);
133         if(*c != 0) {
134                 printf("incorrect value\n");
135                 return;
136         }
137         if(*count == 0) {
138                 count2 = 1;
139         } else {
140                 count2 = strtoul(count, &c, 0);
141                 if(*c != 0) {
142                         printf("incorrect count\n");
143                         return;
144                 }
145         }
146         for (i=0;i<count2;i++) *addr2++ = value2;
147 }
148
149 static int lscb(const char *filename, const char *longname, void *param)
150 {
151         printf("%12s [%s]\n", filename, longname);
152         return 1;
153 }
154
155 static void ls()
156 {
157         cffat_init();
158         cffat_list_files(lscb, NULL);
159         cffat_done();
160 }
161
162 static void render(const char *filename)
163 {
164         if(*filename == 0) {
165                 printf("render <filename>\n");
166                 return;
167         }
168
169         /* TODO */
170 }
171
172 static void spam()
173 {
174         spam_enabled = !spam_enabled;
175         if(spam_enabled)
176                 printf("Advertising enabled\n");
177         else
178                 printf("Advertising disabled\n");
179 }
180
181 static void stats()
182 {
183         int hours, mins, secs;
184         struct timestamp ts;
185         unsigned int amat;
186         unsigned int netbw;
187         unsigned int occupancy;
188
189         time_get(&ts);
190         secs = ts.sec;
191         mins = secs/60;
192         secs -= mins*60;
193         hours = mins/60;
194         mins -= hours*60;
195         printf("Uptime: %02d:%02d:%02d  FPS: %d  CPU:%3d%%\n", hours, mins, secs,
196                 rpipe_fps(),
197                 cpustats_load());
198
199         amat = memstat_amat();
200         netbw = memstat_net_bandwidth();
201         occupancy = memstat_occupancy();
202         if(occupancy != 0) {
203                 printf("Net memory bandwidth : %d Mbps\n", netbw);
204                 printf("Memory bus occupancy : %d%%\n", occupancy);
205                 printf("Avg. mem. access time: %d.%02d cycles\n", amat/100, amat%100);
206         }
207 }
208
209 static void help()
210 {
211         puts("Milkymist demo firmware\n");
212         puts("Available commands:");
213         puts("ls         - list files on the memory card");
214         puts("render     - start rendering a patch");
215         puts("irender    - input patch equations interactively");
216         puts("stop       - stop renderer");
217         puts("spam       - start/stop advertising");
218         puts("stats      - print system stats");
219         puts("reboot     - system reset");
220 }
221
222 static void cpucfg()
223 {
224         unsigned long cpu_cfg;
225         
226         __asm__ volatile(
227                 "rcsr %0, cfg\n\t"
228                 : "=r"(cpu_cfg)
229         );
230         printf("CPU config word: 0x%08x\n", cpu_cfg);
231         printf("Options: ");
232         if(cpu_cfg & 0x001) printf("M ");
233         if(cpu_cfg & 0x002) printf("D ");
234         if(cpu_cfg & 0x004) printf("S ");
235         if(cpu_cfg & 0x008) printf("X ");
236         if(cpu_cfg & 0x010) printf("U ");
237         if(cpu_cfg & 0x020) printf("CC ");
238         if(cpu_cfg & 0x040) printf("DC ");
239         if(cpu_cfg & 0x080) printf("IC ");
240         if(cpu_cfg & 0x100) printf("G ");
241         if(cpu_cfg & 0x200) printf("H ");
242         if(cpu_cfg & 0x400) printf("R ");
243         if(cpu_cfg & 0x800) printf("J ");
244         printf("\n");
245         printf("IRQs: %d\n", (cpu_cfg >> 12) & 0x3F);
246         printf("Breakpoints: %d\n", (cpu_cfg >> 18) & 0x0F);
247         printf("Watchpoints: %d\n", (cpu_cfg >> 22) & 0x0F);
248         printf("CPU revision: %d\n", cpu_cfg >> 26);
249 }
250
251 static void loadpic(const char *filename)
252 {
253         int size;
254         
255         if(*filename == 0) {
256                 printf("loadpic <filename>\n");
257                 return;
258         }
259
260         if(!cffat_init()) return;
261         if(!cffat_load(filename, (void *)vga_backbuffer, vga_hres*vga_vres*2, &size)) return;
262         cffat_done();
263
264         vga_swap_buffers();
265 }
266
267 static void checker()
268 {
269         int x, y;
270
271         for(y=0;y<vga_vres;y++)
272                 for(x=0;x<vga_hres;x++)
273                         if((x/4+y/4) & 1)
274                                 vga_backbuffer[y*vga_hres+x] = 0x0000;
275                         else
276                                 vga_backbuffer[y*vga_hres+x] = 0xffff;
277         vga_swap_buffers();
278 }
279
280 /*
281  * Low-level PFPU test, bypassing driver.
282  * This test should only be run when the driver task queue
283  * is empty.
284  */
285 static struct tmu_vertex mesh[128][128] __attribute__((aligned(8)));
286 static void pfputest()
287 {
288         unsigned int *pfpu_regs = (unsigned int *)CSR_PFPU_DREGBASE;
289         unsigned int *pfpu_code = (unsigned int *)CSR_PFPU_CODEBASE;
290         int x, y;
291         int timeout;
292         unsigned int oldmask;
293
294         /* Do not let the driver get our interrupt */
295         oldmask = irq_getmask();
296         irq_setmask(oldmask & (~IRQ_PFPU));
297
298         for(y=0;y<128;y++)
299                 for(x=0;x<128;x++) {
300                         mesh[y][x].x = 0xdeadbeef;
301                         mesh[y][x].y = 0xdeadbeef;
302                 }
303
304         CSR_PFPU_MESHBASE = (unsigned int)&mesh;
305         //CSR_PFPU_HMESHLAST = 6;
306         //CSR_PFPU_VMESHLAST = 9;
307
308         CSR_PFPU_HMESHLAST = 11;
309         CSR_PFPU_VMESHLAST = 9;
310
311         pfpu_regs[3] = 0x41300000;
312         pfpu_regs[4] = 0x41100000;
313
314         pfpu_code[ 0] = 0x00000300;
315         pfpu_code[ 1] = 0x00040300;
316         pfpu_code[ 2] = 0x00000000;
317         pfpu_code[ 3] = 0x00000003;
318         pfpu_code[ 4] = 0x00000004;
319         pfpu_code[ 5] = 0x000c2080;
320         pfpu_code[ 6] = 0x00000000;
321         pfpu_code[ 7] = 0x00000000;
322         pfpu_code[ 8] = 0x00000000;
323         pfpu_code[ 9] = 0x00000005;
324         pfpu_code[10] = 0x00142b80;
325
326         CSR_PFPU_CTL = PFPU_CTL_START;
327         printf("Waiting for PFPU...\n");
328         timeout = 30;
329         do {
330                 printf("%08x vertices:%d collisions:%d strays:%d last:%08x pc:%04x\n",
331                         CSR_PFPU_CTL, CSR_PFPU_VERTICES, CSR_PFPU_COLLISIONS, CSR_PFPU_STRAYWRITES, CSR_PFPU_LASTDMA, 4*CSR_PFPU_PC);
332         } while((timeout--) && (CSR_PFPU_CTL & PFPU_CTL_BUSY));
333         if(timeout > 0)
334                 printf("OK\n");
335         else
336                 printf("Timeout\n");
337
338         asm volatile( /* Invalidate Level-1 data cache */
339                 "wcsr DCC, r0\n"
340                 "nop\n"
341         );
342
343         printf("Result:\n");
344         for(y=0;y<10;y++) {
345                 for(x=0;x<12;x++)
346                         printf("%08x ", mesh[y][x].x);
347                 printf("\n");
348         }
349         printf("\n");
350         for(y=0;y<10;y++) {
351                 for(x=0;x<12;x++)
352                         printf("%08x ", mesh[y][x].y);
353                 printf("\n");
354         }
355
356         printf("Program:\n");
357         for(x=0;x<11;x++)
358                 printf("%08x ", pfpu_code[x]);
359         printf("\n");
360
361         CSR_PFPU_CTL = 0; /* Ack interrupt */
362         irq_ack(IRQ_PFPU);
363         irq_setmask(oldmask);
364 }
365
366 static void tmutest_callback(struct tmu_td *td)
367 {
368         int *complete;
369         complete = (int *)td->user;
370         *complete = 1;
371 }
372
373 static void tmutest()
374 {
375         int x, y;
376         static struct tmu_vertex srcmesh[TMU_MESH_MAXSIZE][TMU_MESH_MAXSIZE] __attribute__((aligned(8)));
377         struct tmu_td td;
378         volatile int complete;
379
380         for(y=0;y<=32;y++)
381                 for(x=0;x<=32;x++) {
382                         srcmesh[y][x].x = (10*x) << TMU_FIXEDPOINT_SHIFT;
383                         srcmesh[y][x].y = (7*y) << TMU_FIXEDPOINT_SHIFT;
384                 }
385
386         td.flags = 0;
387         td.hmeshlast = 32;
388         td.vmeshlast = 32;
389         td.brightness = TMU_BRIGHTNESS_MAX;
390         td.chromakey = 0;
391         td.vertices = &srcmesh[0][0];
392         td.texfbuf = vga_frontbuffer;
393         td.texhres = vga_hres;
394         td.texvres = vga_vres;
395         td.texhmask = TMU_MASK_FULL;
396         td.texvmask = TMU_MASK_FULL;
397         td.dstfbuf = vga_backbuffer;
398         td.dsthres = vga_hres;
399         td.dstvres = vga_vres;
400         td.dsthoffset = 0;
401         td.dstvoffset = 0;
402         td.dstsquarew = vga_hres/32;
403         td.dstsquareh = vga_vres/32;
404         td.alpha = TMU_ALPHA_MAX;
405         
406         td.callback = tmutest_callback;
407         td.user = (void *)&complete;
408
409         complete = 0;
410         flush_bridge_cache();
411         tmu_submit_task(&td);
412         while(!complete);
413         vga_swap_buffers();
414 }
415
416 static unsigned short original[640*480*2] __attribute__((aligned(2)));
417
418 static void tmudemo()
419 {
420         int size;
421         static struct tmu_vertex srcmesh[TMU_MESH_MAXSIZE][TMU_MESH_MAXSIZE] __attribute__((aligned(8)));
422         struct tmu_td td;
423         volatile int complete;
424         int w, speed;
425         int mindelta, xdelta, ydelta;
426
427         if(!cffat_init()) return;
428         if(!cffat_load("lena.raw", (void *)original, vga_hres*vga_vres*2, &size)) return;
429         cffat_done();
430
431         printf("done\n");
432         
433         speed = 0;
434         w = 512 << TMU_FIXEDPOINT_SHIFT;
435
436         xdelta = 0;
437         ydelta = 0;
438         while(1) {
439                 srcmesh[0][0].x = xdelta;
440                 srcmesh[0][0].y = ydelta;
441                 srcmesh[0][1].x = w+xdelta;
442                 srcmesh[0][1].y = ydelta;
443                 srcmesh[1][0].x = xdelta;
444                 srcmesh[1][0].y = w+ydelta;
445                 srcmesh[1][1].x = w+xdelta;
446                 srcmesh[1][1].y = w+ydelta;
447
448                 if(CSR_GPIO_IN & GPIO_DIP6) {
449                         if(CSR_GPIO_IN & GPIO_PBN)
450                                 ydelta += 16;
451                         if(CSR_GPIO_IN & GPIO_PBS)
452                                 ydelta -= 16;
453                         if(CSR_GPIO_IN & GPIO_PBE)
454                                 xdelta += 16;
455                         if(CSR_GPIO_IN & GPIO_PBW)
456                                 xdelta -= 16;
457                 } else {
458                         if(CSR_GPIO_IN & GPIO_PBN)
459                                 speed += 2;
460                         if(CSR_GPIO_IN & GPIO_PBS)
461                                 speed -= 2;
462                 }
463                 w += speed;
464                 if(w < 1) {
465                         w = 1;
466                         speed = 0;
467                 }
468                 if(xdelta > ydelta)
469                         mindelta = ydelta;
470                 else
471                         mindelta = xdelta;
472                 if(w > ((TMU_MASK_FULL >> 1)+mindelta)) {
473                         w = (TMU_MASK_FULL >> 1)+mindelta;
474                         speed = 0;
475                 }
476                 if(speed > 0) speed--;
477                 if(speed < 0) speed++;
478                 
479
480                 td.flags = 0;
481                 td.hmeshlast = 1;
482                 td.vmeshlast = 1;
483                 td.brightness = TMU_BRIGHTNESS_MAX;
484                 td.chromakey = 0;
485                 td.vertices = &srcmesh[0][0];
486                 td.texfbuf = original;
487                 td.texhres = vga_hres;
488                 td.texvres = vga_vres;
489                 td.texhmask = CSR_GPIO_IN & GPIO_DIP7 ? 0x7FFF : TMU_MASK_FULL;
490                 td.texvmask = CSR_GPIO_IN & GPIO_DIP8 ? 0x7FFF : TMU_MASK_FULL;
491                 td.dstfbuf = vga_backbuffer;
492                 td.dsthres = vga_hres;
493                 td.dstvres = vga_vres;
494                 td.dsthoffset = 0;
495                 td.dstvoffset = 0;
496                 td.dstsquarew = vga_hres;
497                 td.dstsquareh = vga_vres;
498                 td.alpha = TMU_ALPHA_MAX;
499
500                 td.callback = tmutest_callback;
501                 td.user = (void *)&complete;
502
503                 complete = 0;
504                 flush_bridge_cache();
505                 CSR_TIMER1_CONTROL = 0;
506                 CSR_TIMER1_COUNTER = 0;
507                 CSR_TIMER1_COMPARE = 0xffffffff;
508                 CSR_TIMER1_CONTROL = TIMER_ENABLE;
509                 tmu_submit_task(&td);
510                 while(!complete);
511                 CSR_TIMER1_CONTROL = 0;
512
513                 if(readchar_nonblock()) {
514                         char c;
515                         c = readchar();
516                         if(c == 'q') break;
517                         if(c == 's') {
518                                 unsigned int t;
519                                 t = CSR_TIMER1_COUNTER;
520                                 printf("Processing cycles: %d (%d Mpixels/s)\n", t, 640*480*100/t);
521                         }
522                 }
523                 vga_swap_buffers();
524         }
525 }
526
527 static void tmubench()
528 {
529         unsigned int oldmask;
530         int i;
531         int zoom;
532         int x, y;
533         static struct tmu_vertex srcmesh[TMU_MESH_MAXSIZE][TMU_MESH_MAXSIZE] __attribute__((aligned(8)));
534         static unsigned short int texture[512*512] __attribute__((aligned(16)));
535         struct tmu_td td;
536         volatile int complete;
537         unsigned int t;
538         int hits, reqs;
539
540         /* Disable slowout */
541         oldmask = irq_getmask();
542         irq_setmask(oldmask & (~IRQ_TIMER1));
543         uart_force_sync(1);
544         
545         for(i=0;i<512*512;i++)
546                 texture[i] = i;
547         flush_bridge_cache();
548
549         for(zoom=0;zoom<16*64*2;zoom++) {
550                 for(y=0;y<=32;y++)
551                         for(x=0;x<=32;x++) {
552                                 srcmesh[y][x].x = zoom*x;
553                                 srcmesh[y][x].y = zoom*y;
554                         }
555
556                 td.flags = 0;
557                 td.hmeshlast = 32;
558                 td.vmeshlast = 32;
559                 td.brightness = TMU_BRIGHTNESS_MAX;
560                 td.chromakey = 0;
561                 td.vertices = &srcmesh[0][0];
562                 td.texfbuf = texture;
563                 td.texhres = 512;
564                 td.texvres = 512;
565                 td.texhmask = 0x7FFF;
566                 td.texvmask = 0x7FFF;
567                 td.dstfbuf = vga_backbuffer;
568                 td.dsthres = vga_hres;
569                 td.dstvres = vga_vres;
570                 td.dsthoffset = 0;
571                 td.dstvoffset = 0;
572                 td.dstsquarew = vga_hres/32;
573                 td.dstsquareh = vga_vres/32;
574                 td.alpha = TMU_ALPHA_MAX;
575
576                 td.callback = tmutest_callback;
577                 td.user = (void *)&complete;
578
579                 complete = 0;
580                 CSR_TIMER1_CONTROL = 0;
581                 CSR_TIMER1_COUNTER = 0;
582                 CSR_TIMER1_COMPARE = 0xffffffff;
583                 CSR_TIMER1_CONTROL = TIMER_ENABLE;
584                 tmu_submit_task(&td);
585                 while(!complete);
586                 t = CSR_TIMER1_COUNTER;
587                 hits = CSR_TMU_HIT_A + CSR_TMU_HIT_B + CSR_TMU_HIT_C + CSR_TMU_HIT_D;
588                 reqs = CSR_TMU_REQ_A + CSR_TMU_REQ_B + CSR_TMU_REQ_C + CSR_TMU_REQ_D;
589                 printf("%d,%d,%d\n", t, hits, reqs);
590                 vga_swap_buffers();
591         }
592
593         irq_ack(IRQ_TIMER1);
594         irq_setmask(oldmask);
595         uart_force_sync(0);
596 }
597
598 static short audio_buffer1[SND_MAX_NSAMPLES*2];
599 static short audio_buffer2[SND_MAX_NSAMPLES*2];
600 static short audio_buffer3[SND_MAX_NSAMPLES*2];
601 static short audio_buffer4[SND_MAX_NSAMPLES*2];
602
603 static void record_callback(short *buffer, void *user)
604 {
605         snd_play_refill(buffer);
606 }
607
608 static void play_callback(short *buffer, void *user)
609 {
610         snd_record_refill(buffer);
611 }
612
613 static void echo()
614 {
615         if(snd_play_active()) {
616                 snd_record_stop();
617                 snd_play_stop();
618                 printf("Digital Echo demo stopped\n");
619         } else {
620                 int i;
621
622                 snd_play_empty();
623                 for(i=0;i<AC97_MAX_DMASIZE/2;i++) {
624                         audio_buffer1[i] = 0;
625                         audio_buffer2[i] = 0;
626                 }
627                 snd_play_refill(audio_buffer1);
628                 snd_play_refill(audio_buffer2);
629                 snd_play_start(play_callback, SND_MAX_NSAMPLES, NULL);
630
631                 snd_record_empty();
632                 snd_record_refill(audio_buffer3);
633                 snd_record_refill(audio_buffer4);
634                 snd_record_start(record_callback, SND_MAX_NSAMPLES, NULL);
635                 printf("Digital Echo demo started\n");
636         }
637 }
638
639 static char *get_token(char **str)
640 {
641         char *c, *d;
642
643         c = (char *)strchr(*str, ' ');
644         if(c == NULL) {
645                 d = *str;
646                 *str = *str+strlen(*str);
647                 return d;
648         }
649         *c = 0;
650         d = *str;
651         *str = c+1;
652         return d;
653 }
654
655 static int irender;
656
657 static void do_command(char *c)
658 {
659         if(irender) {
660                 if(*c == 0) {
661                         irender = 0;
662                         renderer_idone();
663                 } else
664                         renderer_iinput(c);
665         } else {
666                 char *command, *param1, *param2, *param3;
667
668                 command = get_token(&c);
669                 param1 = get_token(&c);
670                 param2 = get_token(&c);
671                 param3 = get_token(&c);
672
673                 if(strcmp(command, "mr") == 0) mr(param1, param2);
674                 else if(strcmp(command, "mw") == 0) mw(param1, param2, param3);
675                 else if(strcmp(command, "ls") == 0) ls();
676                 else if(strcmp(command, "flush") == 0) flush_bridge_cache();
677                 else if(strcmp(command, "render") == 0) render(param1);
678                 else if(strcmp(command, "irender") == 0) {
679                         renderer_istart();
680                         irender = 1;
681                 } else if(strcmp(command, "stop") == 0) renderer_stop();
682                 else if(strcmp(command, "spam") == 0) spam();
683                 else if(strcmp(command, "stats") == 0) stats();
684                 else if(strcmp(command, "reboot") == 0) reboot();
685                 else if(strcmp(command, "help") == 0) help();
686
687                 /* Test functions and hacks */
688                 else if(strcmp(command, "cpucfg") == 0) cpucfg();
689                 else if(strcmp(command, "loadpic") == 0) loadpic(param1);
690                 else if(strcmp(command, "checker") == 0) checker();
691                 else if(strcmp(command, "pfputest") == 0) pfputest();
692                 else if(strcmp(command, "tmutest") == 0) tmutest();
693                 else if(strcmp(command, "tmudemo") == 0) tmudemo();
694                 else if(strcmp(command, "tmubench") == 0) tmubench();
695                 else if(strcmp(command, "echo") == 0) echo();
696
697                 else if(strcmp(command, "") != 0) printf("Command not found: '%s'\n", command);
698         }
699 }
700
701 static char command_buffer[512];
702 static unsigned int command_index;
703
704 static void prompt()
705 {
706         if(irender)
707                 putsnonl("\e[1mpatch% \e[0m");
708         else
709                 putsnonl("\e[1m% \e[0m");
710 }
711
712 void shell_init()
713 {
714         prompt();
715         command_index = 0;
716         irender = 0;
717 }
718
719 void shell_input(char c)
720 {
721         cpustats_enter();
722         switch(c) {
723                 case 0x7f:
724                 case 0x08:
725                         if(command_index > 0) {
726                                 command_index--;
727                                 putsnonl("\x08 \x08");
728                         }
729                         break;
730                 case '\r':
731                 case '\n':
732                         command_buffer[command_index] = 0x00;
733                         putsnonl("\n");
734                         command_index = 0;
735                         do_command(command_buffer);
736                         prompt();
737                         break;
738                 default:
739                         if(command_index < (sizeof(command_buffer)-1)) {
740                                 writechar(c);
741                                 command_buffer[command_index] = c;
742                                 command_index++;
743                         }
744                         break;
745         }
746         cpustats_leave();
747 }