1 // implementation of the minimal C SDK for KhiCAS
2 int (*shutdown)()=0;
3 
4 short shutdown_state=0;
5 short exam_mode=0;
6 unsigned exam_start=0; // RTC start
7 int exam_duration=0;
8 // <0: indicative duration, ==0 time displayed during exam, >0 end exam_mode after
9 const int exam_bg1=0x4321,exam_bg2=0x1234;
exam_bg()10 int exam_bg(){
11   return exam_mode?(exam_duration>0?exam_bg1:exam_bg2):0x0;
12 }
13 
14 #ifdef NSPIRE_NEWLIB
15 // NB changes for the nspire cx ii
16 // on_key_pressed() should be modified (returns always true)
17 // https://hackspire.org/index.php?title=Memory-mapped_I/O_ports_on_CX_II#90140000_-_Power_management
18 // cx ii power management 0x90140000,
19 // cx 900B0018 (R/W), 900B0020 (?)
20 // cx ii 90140050 (R/W): Disable bus access to peripherals. Reads will just return the last word read from anywhere in the address range, and writes will be ignored.
21 // cx 900F0020 (R/W): LCD contrast/backlight. Valid range for contrast: 0x11a to 0x1ce; normal value is 0x174. However, it can range from 0x100 (backlight off) to about 0x1d0 (about max brightness).
22 // -> cx ii The OS controls the LCD backlight by writing to 90130018.
23 #include "os.h" // Ndless/ndless-sdk/include/os.h
24 #include <unistd.h>
25 #include <stdio.h>
26 #include <dirent.h>
27 #include <ngc.h>
28 #include "k_defs.h"
29 
30 
c_rgb565to888(int c)31 int c_rgb565to888(int c){
32   c &= 0xffff;
33   int r=(c>>11)&0x1f,g=(c>>5)&0x3f,b=c&0x1f;
34   return (r<<19)|(g<<10)|(b<<3);
35 }
36 
37 const int nspire_statusarea=18;
38 bool nspireemu=false;
39 
waitforvblank()40 bool waitforvblank(){
41 }
42 
back_key_pressed()43 bool back_key_pressed(){
44   return isKeyPressed(KEY_NSPIRE_DEL);
45 }
46 // next 3 functions may be void if not inside a window class hierarchy
os_show_graph()47 void os_show_graph(){} // show graph inside Python shell (Numworks), not used
os_hide_graph()48 void os_hide_graph(){} // hide graph, not used anymore
os_redraw()49 void os_redraw(){} // force redraw of window class hierarchy
50 
os_set_angle_unit(int mode)51 bool os_set_angle_unit(int mode){
52   return false;
53 }
54 
os_get_angle_unit()55 int os_get_angle_unit(){
56   return 0;
57 }
58 
millis()59 double millis(){
60   unsigned NSPIRE_RTC_ADDR=0x90090000;
61   unsigned t1= * (volatile unsigned *) NSPIRE_RTC_ADDR;
62   return 1000.0*t1;
63 }
64 
get_hms(int * h,int * m,int * s)65 void get_hms(int *h,int *m,int *s){
66   unsigned NSPIRE_RTC_ADDR=0x90090000;
67   unsigned t1= * (volatile unsigned *) NSPIRE_RTC_ADDR;
68   if (exam_mode){
69     unsigned t=t1-exam_start;
70     if (exam_duration>0 && t>exam_duration){
71       ;//set_exam_mode(0);
72     }
73     else {
74       if (exam_duration>0)
75 	t1=exam_duration-t;
76       else {
77 	if (exam_duration<0 && t<-exam_duration)
78 	  t1=-exam_duration-t;
79       }
80     }
81   }
82   unsigned d=t1/86400;
83   *s=t1%86400;
84   *h=*s/3600;
85   *m=(*s-3600* *h)/60;
86   *s%=60;
87 }
88 
os_wait_1ms(int ms)89 void os_wait_1ms(int ms){
90   msleep(ms);
91 }
92 
file_exists(const char * filename)93 bool file_exists(const char * filename){
94   if (access(filename,R_OK))
95     return false;
96 }
97 
erase_file(const char * filename)98 bool erase_file(const char * filename){
99   return remove(filename)==0;
100 }
101 
102 char nspire_filebuf[NSPIRE_FILEBUFFER];
103 
read_file(const char * filename)104 const char * read_file(const char * filename){
105   if (exam_mode &&
106       (strcmp(filename,"session.xw")!=0 &&
107        strcmp(filename,"session.xw.tns")!=0 &&
108        strcmp(filename,"session.py")!=0 &&
109        strcmp(filename,"session.py.tns")!=0
110        )
111       )
112     return 0;
113   FILE * f = fopen(filename,"r");
114   if (!f) return 0;
115   fseek(f,0L,SEEK_END);
116   unsigned s = ftell(f);
117   fseek(f,0L,SEEK_SET);
118   if (s>NSPIRE_FILEBUFFER-1){
119     fclose(f);
120     return 0;
121   }
122   for (int i=0;i<s;++i){
123     if (feof(f))
124       break;
125     nspire_filebuf[i]=fgetc(f);
126   }
127   nspire_filebuf[s]=0;
128   fclose(f);
129   return nspire_filebuf;
130 }
131 
write_file(const char * filename,const char * s,size_t len)132 bool write_file(const char * filename,const char * s,size_t len){
133   if (exam_mode &&
134       (strcmp(filename,"session.xw")!=0 &&
135        strcmp(filename,"session.xw.tns")!=0 &&
136        strcmp(filename,"session.py")!=0 &&
137        strcmp(filename,"session.py.tns")!=0
138        )
139       )
140     return false;
141   FILE * f=fopen(filename,"w");
142   if (!f) return false;
143   if (!len) len=strlen(s);
144   for (int i=0;i<len;++i)
145     fputc(s[i],f);
146   fclose(f);
147   return true;
148 }
149 
150 #define FILENAME_MAXRECORDS 64
151 char os_filenames[32][FILENAME_MAXRECORDS];
os_file_browser(const char ** filenames,int maxrecords,const char * extension)152 int os_file_browser(const char ** filenames,int maxrecords,const char * extension){
153   DIR *dp;
154   struct dirent *ep;
155   if (maxrecords>FILENAME_MAXRECORDS-1)
156     maxrecords=FILENAME_MAXRECORDS-1;
157   dp = opendir (".");
158   if (dp == NULL){
159     filenames[0]=0;
160     return 0;
161   }
162   int cur=0;
163   while ( (ep = readdir (dp)) && cur<maxrecords){
164     const char * s_=ep->d_name,*ext=0;
165     int l=strlen(s_),j;
166     char s[l+1];
167     strcpy(s,s_);
168     for (j=l-1;j>0;--j){
169       if (s[j]=='.'){
170 	ext=s+j+1;
171 	break;
172       }
173     }
174     if (ext && strcmp(ext,"tns")==0){
175       s[j]=0;
176       for (;j>0;--j){
177 	if (s[j]=='.'){
178 	  ext=s+j+1;
179 	  break;
180 	}
181       }
182     }
183     if (ext && strcmp(ext,extension)==0){
184       if (exam_mode &&
185 	  (strcmp(s_,"session.xw")!=0 &&
186 	   strcmp(s_,"session.xw.tns")!=0 &&
187 	   strcmp(s_,"session.py")!=0 &&
188 	   strcmp(s_,"session.py.tns")!=0
189 	   )
190 	  )
191 	continue;
192       strcpy(os_filenames[cur],s_);
193       filenames[cur]=os_filenames[cur];
194       ++cur;
195     }
196   }
197   closedir (dp);
198   // qsort would be faster for large n, but here n<FILENAME_MAXRECORDS
199   for (;;){
200     bool finished=true;
201     for (int i=1;i<cur;++i){
202       if (strcmp(filenames[i-1],filenames[i])>0){
203 	finished=false;
204 	const char * tmp=filenames[i-1];
205 	filenames[i-1]=filenames[i];
206 	filenames[i]=tmp;
207       }
208     }
209     if (finished)
210       break;
211   }
212   filenames[cur]=NULL;
213   return cur;
214 }
215 
216 Gc nspire_gc=0;
217 
get_gc()218 Gc * get_gc(){
219   if (!nspire_gc){
220     nspire_gc=gui_gc_global_GC();
221     gui_gc_setRegion(nspire_gc, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
222     gui_gc_begin(nspire_gc);
223   }
224   return &nspire_gc;
225 }
226 
os_set_pixel(int x,int y,int c)227 void os_set_pixel(int x,int y,int c){
228   get_gc();
229   gui_gc_setColor(nspire_gc,c_rgb565to888(c));
230   gui_gc_drawRect(nspire_gc,x,y+nspire_statusarea,1,1);
231 }
232 
os_fill_rect(int x,int y,int w,int h,int c)233 void os_fill_rect(int x,int y,int w,int h,int c){
234   get_gc();
235   gui_gc_setColor(nspire_gc,c_rgb565to888(c));
236   gui_gc_fillRect(nspire_gc,x,y+nspire_statusarea,w,h);
237 }
238 
os_get_pixel(int x,int y)239 int os_get_pixel(int x,int y){
240   if (x<0 || x>=SCREEN_WIDTH || y<0 || y>=SCREEN_HEIGHT)
241     return -1;
242 #if 1
243   get_gc();
244   char ** off_buff = ((((char *****)nspire_gc)[9])[0])[0x8];
245   int res = *(unsigned short *) (off_buff[y+nspire_statusarea] + 2*x);
246   return res;
247 #else
248   unsigned short * addr=*(unsigned short **) 0xC0000010;
249   int r=addr[(y+nspire_statusarea)*SCREEN_WIDTH+x];
250   return r;
251 #endif
252 }
253 
nspire_draw_string(int x,int y,int c,int bg,int f,const char * s,bool fake)254 int nspire_draw_string(int x,int y,int c,int bg,int f,const char * s,bool fake){
255   // void ascii2utf16(void *buf, const char *str, int max_size): converts the UTF-8 string str to the UTF-16 string buf of size max_size.
256   int l=strlen(s);
257   char utf16[2*l+2];
258   ascii2utf16(utf16,s,l);
259   utf16[2*l]=0;
260   utf16[2*l+1]=0;
261   get_gc();
262   gui_gc_setFont(nspire_gc,f);
263   int dx=gui_gc_getStringWidth(nspire_gc, f, utf16, 0, l) ;
264   if (fake)
265     return x+dx;
266   int dy=17;
267   if (f==Regular9)
268     dy=13;
269   if (f==Regular11)
270     dy=16;
271   gui_gc_setColor(nspire_gc,c_rgb565to888(bg));
272   gui_gc_fillRect(nspire_gc,x,y,dx,dy);
273   gui_gc_setColor(nspire_gc,c_rgb565to888(c));
274   //gui_gc_setPen(nspire_gc, GC_PS_MEDIUM, GC_PM_SMOOTH);
275   gui_gc_drawString(nspire_gc, utf16, x, y-1, GC_SM_NORMAL | GC_SM_TOP); // normal mode
276   return x+dx;
277 }
278 
os_draw_string(int x,int y,int c,int bg,const char * s,bool fake)279 int os_draw_string(int x,int y,int c,int bg,const char * s,bool fake){
280   get_gc();
281   gui_gc_clipRect(nspire_gc,0,nspire_statusarea,SCREEN_WIDTH,SCREEN_HEIGHT-nspire_statusarea,0);
282   int i=nspire_draw_string(x,y+nspire_statusarea,c,bg,Regular12,s,fake);
283   gui_gc_clipRect(nspire_gc,0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GC_CRO_RESET);
284   return i;
285 }
os_draw_string_small(int x,int y,int c,int bg,const char * s,bool fake)286 int os_draw_string_small(int x,int y,int c,int bg,const char * s,bool fake){
287   get_gc();
288   gui_gc_clipRect(nspire_gc,0,nspire_statusarea,SCREEN_WIDTH,SCREEN_HEIGHT-nspire_statusarea,GC_CRO_SET);
289   int i=nspire_draw_string(x,y+nspire_statusarea,c,bg,Regular9,s,fake);
290   gui_gc_clipRect(nspire_gc,0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GC_CRO_RESET);
291   return i;
292 }
293 
os_draw_string_medium(int x,int y,int c,int bg,const char * s,bool fake)294 int os_draw_string_medium(int x,int y,int c,int bg,const char * s,bool fake){
295   get_gc();
296   gui_gc_clipRect(nspire_gc,0,nspire_statusarea,SCREEN_WIDTH,SCREEN_HEIGHT-nspire_statusarea,GC_CRO_SET);
297   int i=nspire_draw_string(x,y+nspire_statusarea,c,bg,Regular11,s,fake);
298   gui_gc_clipRect(nspire_gc,0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GC_CRO_RESET);
299   return i;
300 }
301 
statuslinemsg(const char * msg)302 void statuslinemsg(const char * msg){
303   get_gc();
304   int bg=exam_bg();
305   gui_gc_setColor(nspire_gc,c_rgb565to888(bg));
306   gui_gc_fillRect(nspire_gc,0,0,SCREEN_WIDTH,nspire_statusarea);
307   nspire_draw_string(0,0,0xffff,bg,Regular9,msg,false);
308 }
309 
display_time()310 void display_time(){
311   int h,m,s;
312   get_hms(&h,&m,&s);
313   char msg[10];
314   msg[0]=' ';
315   msg[1]='0'+(h/10);
316   msg[2]='0'+(h%10);
317   msg[3]= 'h';
318   msg[4]= ('0'+(m/10));
319   msg[5]= ('0'+(m%10));
320   msg[6]=0;
321   //msg[6]= 'm';
322   //msg[7] = ('0'+(s/10));
323   //msg[8] = ('0'+(s%10));
324   //msg[9]=0;
325   int bg=exam_bg();
326   gui_gc_setColor(nspire_gc,c_rgb565to888(bg));
327   gui_gc_fillRect(nspire_gc,270,0,SCREEN_WIDTH-270,nspire_statusarea);
328   nspire_draw_string(270,0,0xffff,bg,Regular9,msg,false);
329 }
330 
sync_screen()331 void sync_screen(){
332   get_gc();
333   //gui_gc_finish(nspire_gc);
334   gui_gc_blit_to_screen(nspire_gc);
335   msleep(10);
336   //nspire_gc=0;
337   // gui_gc_begin(nspire_gc);
338 }
339 
340 // Nspire peripheral reset :
341 // https://github.com/nDroidProject/nDroid-bootloader/blob/master/kernel.c
342 // https://hackspire.org/index.php?title=Memory-mapped_I/O_ports_on_CX#CC000000_-_SHA-256_hash_generator
343 // hardware ports
344 // https://hackspire.org/index.php?title=Memory-mapped_I/O_ports_on_CX
345 
346 bool nspire_shift=false;
347 bool nspire_ctrl=false;
348 bool nspire_select=false;
statusline(int mode)349 void statusline(int mode){
350   char *msg=0;
351   if (nspire_ctrl){
352     if (nspire_shift)
353       msg="shift ctrl";
354     else
355       msg="        ctrl";
356   }
357   else {
358     if (nspire_shift)
359       msg="shift";
360     else
361       msg="";
362   }
363   int bg=exam_bg();
364   gui_gc_setColor(nspire_gc,c_rgb565to888(bg));
365   gui_gc_fillRect(nspire_gc,210,0,SCREEN_WIDTH-210,nspire_statusarea);
366   nspire_draw_string(220,0,0xffff,bg,Regular9,msg,false);
367   if (nspireemu)
368     nspire_draw_string(210,0,0xffff,bg,Regular9,"e",false);
369   display_time();
370   if (mode==0)
371     return;
372   sync_screen();
373 }
374 
375 
376 #define SHIFTCTRL(x, y, z) (nspire_ctrl ? (z) : nspire_shift ? (y) : (x))
377 #define SHIFT(x, y) SHIFTCTRL(x, y, x)
378 #define CTRL(x, y) SHIFTCTRL(x, x, y)
379 #define NORMAL(x) SHIFTCTRL(x, x, x)
380 
ascii_get(int * adaptive_cursor_state)381 int ascii_get(int* adaptive_cursor_state){
382   if (isKeyPressed(KEY_NSPIRE_CTRL)){
383     nspire_ctrl=!nspire_ctrl;
384     statusline(0);
385     sync_screen();
386     return -2;
387   }
388   if (isKeyPressed(KEY_NSPIRE_SHIFT)){
389     nspire_shift=!nspire_shift;
390     statusline(0);
391     sync_screen();
392     return -1;
393   }
394   *adaptive_cursor_state = SHIFTCTRL(0, 1, 4);
395   if (isKeyPressed(KEY_NSPIRE_LEFT)|| isKeyPressed(KEY_NSPIRE_LEFTUP) || isKeyPressed(KEY_NSPIRE_DOWNLEFT))		return SHIFTCTRL(KEY_CTRL_LEFT,KEY_SHIFT_LEFT,KEY_LEFT_CTRL);
396   if (isKeyPressed(KEY_NSPIRE_RIGHT)|| isKeyPressed(KEY_NSPIRE_UPRIGHT) || isKeyPressed(KEY_NSPIRE_RIGHTDOWN))		return SHIFTCTRL(KEY_CTRL_RIGHT,KEY_SHIFT_RIGHT,KEY_RIGHT_CTRL);
397   if (isKeyPressed(KEY_NSPIRE_UP))		return SHIFTCTRL(KEY_CTRL_UP,KEY_CTRL_PAGEUP,KEY_UP_CTRL);
398   if (isKeyPressed(KEY_NSPIRE_DOWN))		return SHIFTCTRL(KEY_CTRL_DOWN,KEY_CTRL_PAGEDOWN,KEY_DOWN_CTRL);
399 
400   if (isKeyPressed(KEY_NSPIRE_ESC)) return KEY_CTRL_EXIT ;
401   if (isKeyPressed(KEY_NSPIRE_HOME)) return KEY_CTRL_MENU ;
402   if (isKeyPressed(KEY_NSPIRE_MENU)) return KEY_CTRL_MENU ;
403 
404   // Characters
405   if (isKeyPressed(KEY_NSPIRE_A)) return SHIFTCTRL('a','A',KEY_CTRL_A);
406   if (isKeyPressed(KEY_NSPIRE_B)) return SHIFTCTRL('b','B',KEY_BOOK);
407   if (isKeyPressed(KEY_NSPIRE_C)) return SHIFTCTRL('c','C',KEY_CTRL_CLIP);
408   if (isKeyPressed(KEY_NSPIRE_D)) return SHIFTCTRL('d','D',KEY_CTRL_D);
409   if (isKeyPressed(KEY_NSPIRE_E)) return SHIFTCTRL('e','E',KEY_CTRL_F10);
410   if (isKeyPressed(KEY_NSPIRE_F)) return SHIFTCTRL('f','F',KEY_CTRL_F11);
411   if (isKeyPressed(KEY_NSPIRE_G)) return SHIFTCTRL('g','G',KEY_CTRL_F12);
412   if (isKeyPressed(KEY_NSPIRE_H)) return SHIFTCTRL('h','H',KEY_CTRL_F13);
413   if (isKeyPressed(KEY_NSPIRE_I)) return SHIFTCTRL('i','I',KEY_CTRL_F14);
414   if (isKeyPressed(KEY_NSPIRE_J)) return SHIFT('j','J');
415   if (isKeyPressed(KEY_NSPIRE_K)) return SHIFTCTRL('k','K',KEY_CTRL_AC);
416   if (isKeyPressed(KEY_NSPIRE_L)) return SHIFTCTRL('l','L',KEY_CTRL_F14);
417   if (isKeyPressed(KEY_NSPIRE_M)) return SHIFTCTRL('m','M',KEY_CTRL_CATALOG);
418   if (isKeyPressed(KEY_NSPIRE_N)) return SHIFTCTRL('n','N',KEY_CTRL_N);
419   if (isKeyPressed(KEY_NSPIRE_O)) return SHIFTCTRL('o','O',KEY_SHIFT_OPTN);
420   if (isKeyPressed(KEY_NSPIRE_P)) return SHIFTCTRL('p','P',KEY_CTRL_PRGM);
421   if (isKeyPressed(KEY_NSPIRE_Q)) return SHIFT('q','Q');
422   if (isKeyPressed(KEY_NSPIRE_R)) return SHIFTCTRL('r','R',KEY_CTRL_R);
423   if (isKeyPressed(KEY_NSPIRE_S)) return SHIFTCTRL('s','S',KEY_CTRL_S);
424   if (isKeyPressed(KEY_NSPIRE_T)) return SHIFTCTRL('t','T',KEY_CTRL_T);
425   if (isKeyPressed(KEY_NSPIRE_U)) return SHIFTCTRL('u','U',KEY_CTRL_F13);
426   if (isKeyPressed(KEY_NSPIRE_V)) return SHIFTCTRL('v','V',KEY_CTRL_PASTE);
427   if (isKeyPressed(KEY_NSPIRE_W)) return SHIFT('w','W');
428   if (isKeyPressed(KEY_NSPIRE_X)) return SHIFTCTRL('x','X',KEY_CTRL_CUT);
429   if (isKeyPressed(KEY_NSPIRE_Y)) return SHIFT('y','Y');
430   if (isKeyPressed(KEY_NSPIRE_Z)) return SHIFTCTRL('z','Z',KEY_CTRL_UNDO);
431 
432   // Numbers
433   if (nspireemu){ // for firebird, redefine ctrl
434     if (isKeyPressed(KEY_NSPIRE_0)) return SHIFTCTRL('0',KEY_CTRL_F10,')');
435     if (isKeyPressed(KEY_NSPIRE_1)) return SHIFTCTRL('1',KEY_CTRL_F1,'!');
436     if (isKeyPressed(KEY_NSPIRE_2)) return SHIFTCTRL('2',KEY_CTRL_F2,'@');
437     if (isKeyPressed(KEY_NSPIRE_3)) return SHIFTCTRL('3',KEY_CTRL_F3,'#');
438     if (isKeyPressed(KEY_NSPIRE_4)) return SHIFTCTRL('4',KEY_CTRL_F4,'$');
439     if (isKeyPressed(KEY_NSPIRE_5)) return SHIFTCTRL('5',KEY_CTRL_F5,'%');
440     if (isKeyPressed(KEY_NSPIRE_6)) return SHIFTCTRL('6',KEY_CTRL_F6,'^');
441     if (isKeyPressed(KEY_NSPIRE_7)) return SHIFTCTRL('7',KEY_CTRL_F7,'&');
442     if (isKeyPressed(KEY_NSPIRE_8)) return SHIFTCTRL('8',KEY_CTRL_F8,'*');
443     if (isKeyPressed(KEY_NSPIRE_9)) return SHIFTCTRL('9',KEY_CTRL_F9,'(');
444   }
445   else {
446     if (isKeyPressed(KEY_NSPIRE_0)) return SHIFTCTRL('0',KEY_CTRL_F10,KEY_CTRL_F10);
447     if (isKeyPressed(KEY_NSPIRE_1)) return SHIFTCTRL('1',KEY_CTRL_F1,KEY_CTRL_F1);
448     if (isKeyPressed(KEY_NSPIRE_2)) return SHIFTCTRL('2',KEY_CTRL_F2,KEY_CTRL_F2);
449     if (isKeyPressed(KEY_NSPIRE_3)) return SHIFTCTRL('3',KEY_CTRL_F3,KEY_CTRL_F3);
450     if (isKeyPressed(KEY_NSPIRE_4)) return SHIFTCTRL('4',KEY_CTRL_F4,KEY_CTRL_F4);
451     if (isKeyPressed(KEY_NSPIRE_5)) return SHIFTCTRL('5',KEY_CTRL_F5,KEY_CTRL_F5);
452     if (isKeyPressed(KEY_NSPIRE_6)) return SHIFTCTRL('6',KEY_CTRL_F6,KEY_CTRL_F6);
453     if (isKeyPressed(KEY_NSPIRE_7)) return SHIFTCTRL('7',KEY_CTRL_F7,KEY_CTRL_F7);
454     if (isKeyPressed(KEY_NSPIRE_8)) return SHIFTCTRL('8',KEY_CTRL_F8,KEY_CTRL_F8);
455     if (isKeyPressed(KEY_NSPIRE_9)) return SHIFTCTRL('9',KEY_CTRL_F9,KEY_CTRL_F9);
456   }
457 
458   // Symbols
459   if (isKeyPressed(KEY_NSPIRE_FRAC)) return SHIFTCTRL(KEY_EQW_TEMPLATE,KEY_AFFECT,KEY_AFFECT);
460   if (isKeyPressed(KEY_NSPIRE_SQU)) return CTRL(KEY_CHAR_SQUARE,KEY_CHAR_ROOT);
461   if (isKeyPressed(KEY_NSPIRE_TENX)) return CTRL(KEY_CHAR_EXPN10,KEY_CHAR_LOG);
462   if (isKeyPressed(KEY_NSPIRE_eEXP)) return CTRL(KEY_CHAR_EXPN,KEY_CHAR_LN);
463   if (isKeyPressed(KEY_NSPIRE_COMMA))		return SHIFTCTRL(',',';',':');
464   if (isKeyPressed(KEY_NSPIRE_PERIOD)) 	return SHIFTCTRL('.',':',KEY_CTRL_F11);
465   if (isKeyPressed(KEY_NSPIRE_COLON))		return NORMAL(':');
466   if (isKeyPressed(KEY_NSPIRE_LP))			return SHIFTCTRL('(',']','[');
467   if (isKeyPressed(KEY_NSPIRE_RP))			return SHIFTCTRL(')','}','{');
468   if (isKeyPressed(KEY_NSPIRE_SPACE))		return SHIFT(' ','_');
469   if (isKeyPressed(KEY_NSPIRE_DIVIDE))
470     return SHIFTCTRL('/','%','\\');
471   if (isKeyPressed(KEY_NSPIRE_MULTIPLY))	return SHIFTCTRL('*','\'','\"');
472   if (isKeyPressed(KEY_NSPIRE_MINUS))		return SHIFTCTRL('-','<', '_');
473   if (isKeyPressed(KEY_NSPIRE_NEGATIVE))	return SHIFTCTRL('-','_',KEY_CHAR_ANS);
474   if (isKeyPressed(KEY_NSPIRE_PLUS))		return SHIFTCTRL('+', '>','>');
475   if (isKeyPressed(KEY_NSPIRE_EQU))		return SHIFTCTRL('=', '|',KEY_CHAR_STORE);
476   if (isKeyPressed(KEY_NSPIRE_LTHAN))		return NORMAL('<');
477   if (isKeyPressed(KEY_NSPIRE_GTHAN))		return NORMAL('>');
478   if (isKeyPressed(KEY_NSPIRE_QUOTE))		return NORMAL('\"');
479   if (isKeyPressed(KEY_NSPIRE_APOSTROPHE))	return NORMAL('\'');
480   if (isKeyPressed(KEY_NSPIRE_QUES))		return SHIFTCTRL('?','|','!');
481   if (isKeyPressed(KEY_NSPIRE_QUESEXCL))	return SHIFTCTRL('?','|','!');
482   if (isKeyPressed(KEY_NSPIRE_BAR))		return NORMAL('|');
483   if (isKeyPressed(KEY_NSPIRE_EXP))		return SHIFT('^',KEY_CHAR_RECIP);
484   if (isKeyPressed(KEY_NSPIRE_EE))		return SHIFTCTRL('&','%', '@');
485   if (isKeyPressed(KEY_NSPIRE_PI)) return KEY_CHAR_PI;
486   if (isKeyPressed(KEY_NSPIRE_FLAG)) return SHIFTCTRL(';',':',KEY_CHAR_IMGNRY);
487   if (isKeyPressed(KEY_NSPIRE_ENTER))		return SHIFTCTRL(KEY_CTRL_OK,'~',KEY_CTRL_EXE);
488   if (isKeyPressed(KEY_NSPIRE_TRIG))		return SHIFTCTRL(KEY_CHAR_SIN,KEY_CHAR_COS,KEY_CHAR_TAN);
489 
490   // Special chars
491   if (isKeyPressed(KEY_NSPIRE_SCRATCHPAD)) return SHIFTCTRL(KEY_CTRL_SETUP,KEY_LOAD,KEY_SAVE);
492   if (isKeyPressed(KEY_NSPIRE_VAR)) return CTRL(KEY_CTRL_VARS,KEY_CHAR_STORE);
493   if (isKeyPressed(KEY_NSPIRE_DOC))		return KEY_CTRL_CATALOG;
494   if (isKeyPressed(KEY_NSPIRE_CAT))		return KEY_BOOK;
495   if (isKeyPressed(KEY_NSPIRE_DEL))		return SHIFTCTRL(KEY_CTRL_DEL,KEY_CTRL_DEL,KEY_CTRL_AC);
496   if (isKeyPressed(KEY_NSPIRE_RET))		return KEY_CTRL_EXE;
497   if (isKeyPressed(KEY_NSPIRE_TAB))		return '\t';
498 
499   return 0;
500 }
501 
iskeydown(int key)502 bool iskeydown(int key){
503   t_key t=KEY_NSPIRE_SPACE;
504   switch (key){
505   case 0:
506     t=KEY_NSPIRE_LEFT;
507     break;
508   case 1:
509     t=KEY_NSPIRE_UP;
510     break;
511   case 2:
512     t=KEY_NSPIRE_DOWN;
513     break;
514   case 3:
515     t=KEY_NSPIRE_RIGHT;
516     break;
517   case 4:
518     t=KEY_NSPIRE_ENTER;
519     break;
520   case 5:
521     t=KEY_NSPIRE_ESC;
522     break;
523   case 6:
524     t=KEY_NSPIRE_HOME;
525     break;
526   case 7:
527     t=KEY_NSPIRE_MENU;
528     break;
529   case 12:
530     t=KEY_NSPIRE_SHIFT;
531     break;
532   case 13:
533     t=KEY_NSPIRE_CTRL;
534     break;
535   case 14:
536     t=KEY_NSPIRE_SCRATCHPAD;
537     break;
538   case 15:
539     t=KEY_NSPIRE_VAR;
540     break;
541   case 16:
542     t=KEY_NSPIRE_DOC;
543     break;
544   case 17:
545     t=KEY_NSPIRE_DEL;
546     break;
547   case 18:
548     t=KEY_NSPIRE_eEXP;
549     break;
550   case 19:
551     t=KEY_NSPIRE_EQU;
552     break;
553   case 20:
554     t=KEY_NSPIRE_TENX;
555     break;
556   case 21:
557     t=KEY_NSPIRE_I;
558     break;
559   case 22:
560     t=KEY_NSPIRE_COMMA;
561     break;
562   case 23:
563     t=KEY_NSPIRE_EXP;
564     break;
565   case 24:
566     t=KEY_NSPIRE_TRIG;
567     break;
568   case 25:
569     t=KEY_NSPIRE_C;
570     break;
571   case 26:
572     t=KEY_NSPIRE_T;
573     break;
574   case 27:
575     t=KEY_NSPIRE_PI;
576     break;
577   case 28:
578     t=KEY_NSPIRE_S;
579     break;
580   case 29:
581     t=KEY_NSPIRE_SQU;
582     break;
583   case 30:
584     t=KEY_NSPIRE_7;
585     break;
586   case 31:
587     t=KEY_NSPIRE_8;
588     break;
589   case 32:
590     t=KEY_NSPIRE_9;
591     break;
592   case 33:
593     t=KEY_NSPIRE_LP;
594     break;
595   case 34:
596     t=KEY_NSPIRE_RP;
597     break;
598   case 36:
599     t=KEY_NSPIRE_4;
600     break;
601   case 37:
602     t=KEY_NSPIRE_5;
603     break;
604   case 38:
605     t=KEY_NSPIRE_6;
606     break;
607   case 39:
608     t=KEY_NSPIRE_MULTIPLY;
609     break;
610   case 40:
611     t=KEY_NSPIRE_DIVIDE;
612     break;
613   case 42:
614     t=KEY_NSPIRE_1;
615     break;
616   case 43:
617     t=KEY_NSPIRE_2;
618     break;
619   case 44:
620     t=KEY_NSPIRE_3;
621     break;
622   case 45:
623     t=KEY_NSPIRE_PLUS;
624     break;
625   case 46:
626     t=KEY_NSPIRE_MINUS;
627     break;
628   case 48:
629     t=KEY_NSPIRE_0;
630     break;
631   case 49:
632     t=KEY_NSPIRE_PERIOD;
633     break;
634   case 50:
635     t=KEY_NSPIRE_EE;
636     break;
637   case 51:
638     t=KEY_NSPIRE_NEGATIVE;
639     break;
640   case 52:
641     t=KEY_NSPIRE_RET;
642     break;
643   }
644   return isKeyPressed(t);
645 }
646 
647 // ? see also ndless-sdk/thirdparty/nspire-io/arch-nspire/nspire.c nio_ascii_get
getkey(int allow_suspend)648 int getkey(int allow_suspend){
649   sync_screen();
650   if (shutdown_state)
651     return KEY_SHUTDOWN;
652   int lastkey=-1;
653   unsigned NSPIRE_RTC_ADDR=0x90090000;
654   static unsigned lastt=0;
655   for (;;){
656     unsigned t1= * (volatile unsigned *) NSPIRE_RTC_ADDR;
657     if (lastt==0)
658       lastt=t1;
659     if (t1-lastt>10){
660       display_time();
661       sync_screen();
662     }
663     bool autosuspend=(t1-lastt>=100);
664     if (allow_suspend && (autosuspend || (nspire_ctrl && on_key_pressed()))){
665       nspire_ctrl=nspire_shift=false;
666       while (!autosuspend && on_key_pressed())
667 	msleep(10);
668       // somewhat OFF by setting LCD to 0
669       unsigned NSPIRE_CONTRAST_ADDR=0x900f0020;
670       unsigned oldval=*(volatile unsigned *)NSPIRE_CONTRAST_ADDR;
671       *(volatile unsigned *)NSPIRE_CONTRAST_ADDR=0x100;
672       static volatile uint32_t *lcd_controller = (volatile uint32_t*) 0xC0000000;
673       lcd_controller[6] &= ~(0b1 << 11);
674       msleep(20);
675       lcd_controller[6] &= ~ 0b1;
676       unsigned offtime=* (volatile unsigned *) NSPIRE_RTC_ADDR;
677       for (int n=0;!on_key_pressed();++n){
678 	msleep(100);
679 	idle();
680 	if (!exam_mode && shutdown
681 	    // && n&0xff==0
682 	    ){
683 	  unsigned curtime=* (volatile unsigned *) NSPIRE_RTC_ADDR;
684 	  if (curtime-offtime>7200){
685 	    shutdown_state=1;
686 	    // after 2 hours, leave KhiCAS
687 	    // that way the OS will really shutdown the calc
688 	    lcd_controller[6] |= 0b1;
689 	    msleep(20);
690 	    lcd_controller[6]|= 0b1 << 11;
691 	    *(volatile unsigned *)NSPIRE_CONTRAST_ADDR=oldval;
692 	    statuslinemsg("Press ON to disable KhiCAS auto shutdown");
693 	    //os_fill_rect(0,0,320,222,0xffff);
694 	    sync_screen();
695 	    int m=0,mmax=150;
696 	    for (;m<mmax;++m){
697 	      if (on_key_pressed())
698 		break;
699 	      msleep(100);
700 	      idle();
701 	    }
702 	    if (m==mmax){
703 	      if (shutdown())
704 		return KEY_SHUTDOWN;
705 	    }
706 	    else {
707 	      shutdown_state=0;
708 	      break;
709 	    }
710 	  }
711 	}
712       }
713       lcd_controller[6] |= 0b1;
714       msleep(20);
715       lcd_controller[6]|= 0b1 << 11;
716       *(volatile unsigned *)NSPIRE_CONTRAST_ADDR=oldval;
717       statusline(0);
718       sync_screen();
719       lastt=* (volatile unsigned *) NSPIRE_RTC_ADDR;
720       continue;
721     }
722     if (!any_key_pressed()){
723       if (nspireemu)
724 	msleep(50); // 100?
725       else // real calculator
726 	msleep(1);
727       continue;
728     }
729     lastt=t1;
730     int cursor_state=0;
731     int i=0;
732     if (isKeyPressed(KEY_NSPIRE_SHIFT)){
733       while (i==0 && isKeyPressed(KEY_NSPIRE_SHIFT)){
734 	if (isKeyPressed(KEY_NSPIRE_LEFT)|| isKeyPressed(KEY_NSPIRE_LEFTUP) || isKeyPressed(KEY_NSPIRE_DOWNLEFT))
735 	  i=KEY_SELECT_LEFT;
736 	if (isKeyPressed(KEY_NSPIRE_RIGHT)|| isKeyPressed(KEY_NSPIRE_UPRIGHT) || isKeyPressed(KEY_NSPIRE_RIGHTDOWN))
737 	  i=KEY_SELECT_RIGHT;
738 	if (isKeyPressed(KEY_NSPIRE_UP))
739 	  i=KEY_SELECT_UP;
740 	if (isKeyPressed(KEY_NSPIRE_DOWN))
741 	  i=KEY_SELECT_DOWN;
742       }
743       if (i!=0){
744 	nspire_select=true;
745       }
746       if (i==0){
747 	nspire_shift=!nspire_shift;
748 	statusline(0);
749 	sync_screen();
750 	i=-1;
751       }
752     }
753     else {
754       if (nspire_select){
755 	nspire_select=nspire_shift=false;
756 	statusline(0);
757 	sync_screen();
758 	continue;
759       }
760       i=ascii_get(&cursor_state);
761     }
762     if (i<0){
763       wait_no_key_pressed();
764       continue;
765     }
766     if (i==KEY_CTRL_N){
767       nspireemu=!nspireemu;
768       nspire_ctrl=nspire_shift=false;
769       statusline(0);
770       sync_screen();
771       continue;
772     }
773     if ( (i>=KEY_CTRL_LEFT && i<=KEY_CTRL_RIGHT) ||
774 	 (i>=KEY_UP_CTRL && i<=KEY_RIGHT_CTRL) ||
775 	 (i>=KEY_SELECT_LEFT && i<=KEY_SELECT_RIGHT) ||
776 	 i==KEY_CTRL_DEL){
777       int delay=(lastkey==i)?5:40,j;
778       for (j=0;j<delay && any_key_pressed();++j){
779 	if (nspireemu)
780 	  msleep(14);
781 	else
782 	  msleep(1);
783       }
784       if (any_key_pressed())
785 	lastkey=i;
786       else
787 	lastkey=-1;
788     }
789     else {
790       wait_no_key_pressed();
791       lastkey=-1;
792     }
793     if (nspire_ctrl || nspire_shift){
794       nspire_ctrl=nspire_shift=false;
795       statusline(0);
796       sync_screen();
797     }
798     return i;
799   }
800   // void send_key_event(struct s_ns_event* eventbuf, unsigned short keycode_asciicode, BOOL is_key_up, BOOL unknown): since r721. Simulate a key event
801 }
802 
803 // void idle(void)
804 // void msleep(unsigned ms)
805 // cfg_register_fileext(const char *ext, const char *prgm): (since v3.1 r797) associate for Ndless the file extension ext (without leading '.') to the program name prgm. Does nothing if the extension is already registered.
806 
GetKey(int * key)807 void GetKey(int * key){
808   *key=getkey(true);
809 }
810 
alphawasactive(int * key)811 bool alphawasactive(int * key){
812   if (*key==KEY_DOWN_CTRL){
813     *key=KEY_CTRL_DOWN;
814     return true;
815   }
816   if (*key==KEY_UP_CTRL){
817     *key=KEY_CTRL_UP;
818     return true;
819   }
820   if (*key==KEY_LEFT_CTRL){
821     *key=KEY_CTRL_LEFT;
822     return true;
823   }
824   if (*key==KEY_RIGHT_CTRL){
825     *key=KEY_CTRL_RIGHT;
826     return true;
827   }
828   return false;
829 }
830 
isalphaactive()831 bool isalphaactive(){
832   return false;//nspire_ctrl;
833 }
834 
lock_alpha()835 void lock_alpha(){
836   //nspire_ctrl=true;
837 }
838 
reset_kbd()839 void reset_kbd(){
840   nspire_ctrl=nspire_shift=false;
841 }
842 
843 bool on_key_enabled=true;
844 
enable_back_interrupt()845 void enable_back_interrupt(){
846   on_key_enabled=true;
847 }
848 
disable_back_interrupt()849 void disable_back_interrupt(){
850   on_key_enabled=false;
851 }
852 #else // NSPIRE_NEWLIB
853 
set_exam_mode(int i)854 void set_exam_mode(int i){
855   exam_mode=i;
856 }
857 #endif // NSPIRE_NEWLIB
858