diff --git a/apps/Makefile b/apps/Makefile index 3aed3073532..b2e67a83283 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -50,6 +50,7 @@ apps_src += $(addprefix apps/,\ exam_mode_configuration_official.cpp:+official \ exam_mode_configuration_non_official.cpp:-official \ global_preferences.cpp \ + host_filemanager.cpp \ i18n.py \ lock_view.cpp \ main.cpp \ diff --git a/apps/calculation/calculation_store.cpp b/apps/calculation/calculation_store.cpp index f1bb4b940c8..eee89ac89ed 100644 --- a/apps/calculation/calculation_store.cpp +++ b/apps/calculation/calculation_store.cpp @@ -7,6 +7,39 @@ #include "../exam_mode_configuration.h" #include +#if defined _FXCG || defined NSPIRE_NEWLIB +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static KDCoordinate dummyHeight(::Calculation::Calculation * c, bool expanded) { + bool b; + Poincare::Layout l = c->createExactOutputLayout(&b); + if (!b) { + l=c->createInputLayout(); + } + KDSize s = l.layoutSize(); + const int bordersize = 10; + int h = s.height() + bordersize; + const int maxheight = 64; + if (h > maxheight) { + return maxheight; + } + return h; +} + +extern void * last_calculation_history; +void * last_calculation_history = 0; +const char * retrieve_calc_history(); + +#endif + using namespace Poincare; using namespace Shared; @@ -21,10 +54,39 @@ CalculationStore::CalculationStore(char * buffer, int size) : { assert(m_buffer != nullptr); assert(m_bufferSize > 0); +#if defined _FXCG || defined NSPIRE_NEWLIB + if (last_calculation_history == 0){ + // Restore from scriptstore + const char * buf=retrieve_calc_history(); + if (buf) { + Shared::GlobalContext globalContext; + char * ptr=(char *)buf; + for (;*ptr;) { + for (;*ptr;++ptr) { + if (*ptr=='\n') { + break; + } + } + char c = *ptr; + *ptr=0; + if (ptr > buf) { + push(buf,&globalContext, dummyHeight); + } + *ptr = c; + ++ptr; + buf = ptr; + } + } + last_calculation_history = (void *) this; + } +#endif } // Returns an expiring pointer to the calculation of index i, and ignore the trash ExpiringPointer CalculationStore::calculationAtIndex(int i) { +#if defined _FXCG || defined NSPIRE_NEWLIB + last_calculation_history = (void *) this; +#endif if (m_trashIndex == -1 || i < m_trashIndex) { return realCalculationAtIndex(i); } else { diff --git a/apps/code/app.cpp b/apps/code/app.cpp index 12833f6f835..8739030871c 100644 --- a/apps/code/app.cpp +++ b/apps/code/app.cpp @@ -5,6 +5,12 @@ #include #include +#if defined _FXCG || defined NSPIRE_NEWLIB +extern "C" int calculator; +extern "C" const int prizm_heap_size; +extern "C" char prizm_heap[]; +#endif + namespace Code { I18n::Message App::Descriptor::name() { @@ -150,7 +156,17 @@ bool App::textInputDidReceiveEvent(InputEventHandler * textInput, Ion::Events::E void App::initPythonWithUser(const void * pythonUser) { if (!m_pythonUser) { - MicroPython::init(m_pythonHeap, m_pythonHeap + k_pythonHeapSize); +#if defined _FXCG || defined NSPIRE_NEWLIB + if (calculator == 1) { // fxcg50 + MicroPython::init( (void *) 0x8c200000, (void *)(0x8c200000+ 0x2e0000)); + } else if (calculator >= 1 && calculator <=4 ) { + MicroPython::init( prizm_heap, prizm_heap+prizm_heap_size); + } else { +#endif + MicroPython::init(m_pythonHeap, m_pythonHeap + k_pythonHeapSize); +#if defined _FXCG || defined NSPIRE_NEWLIB + } +#endif } m_pythonUser = pythonUser; } diff --git a/apps/external/app/sample.c b/apps/external/app/sample.c index dbc13fd9545..a779bf2f42d 100644 --- a/apps/external/app/sample.c +++ b/apps/external/app/sample.c @@ -1,6 +1,15 @@ #include +#if defined _FXCG || defined NSPIRE_NEWLIB +// On the port, we use the Built-in file manager to import files. +void host_filemanager(); +void extapp_main() { + host_filemanager(); +} +#else +// Elsewhere, just draw a rectangle to test the extapp API. void extapp_main() { extapp_pushRectUniform(10, 10, LCD_WIDTH-20, LCD_HEIGHT-20, 0); extapp_msleep(1000); } +#endif diff --git a/apps/host_filemanager.cpp b/apps/host_filemanager.cpp new file mode 100644 index 00000000000..9c0bb70465e --- /dev/null +++ b/apps/host_filemanager.cpp @@ -0,0 +1,859 @@ +// #include +// // #include "../apps_container.h" +// #include "stddef.h" +// #include "string.h" +// #include "math.h" +// #include "app.h" +#include +#include "../apps_container.h" +#include "../global_preferences.h" +#include "../exam_mode_configuration.h" + +extern "C" { +#include +} + +#ifdef NSPIRE_NEWLIB +const char * storage_name="/documents/nwstore.nws.tns"; +#else +const char * storage_name="nwstore.nws"; +#endif + +const char * calc_storage_name="nwcalc.txt"; +void * storage_address(); // ion/src/simulator/shared/platform_info.cpp +const char * retrieve_calc_history(); +#if defined _FXCG || defined NSPIRE_NEWLIB +bool save_calc_history(); +void display_host_help(); +int load_state(const char * fname); +#endif + +// Additional code by B. Parisse for host file system support and persistence +// on Casio Graph 90/FXCG50 and TI Nspire +void erase_record(const char * name){ + unsigned char * ptr=(unsigned char *)storage_address(); + for (ptr+=4;;){ // skip header + size_t L=ptr[1]*256+ptr[0]; + if (L==0) return; + if (strcmp((const char *)ptr+2,name)==0){ + unsigned char * newptr=ptr; + int S=0,erased=L; + ptr+=L; + for (;;){ + L=ptr[1]*256+ptr[0]; + if (L==0){ + for (int i=0;inewptr+S) + memmove(newptr+S,ptr,L); + S+=L; + ptr+=L; + } + return; + } + ptr+=L; + } +} + + +// record filtering on read +void filter(unsigned char * ptr){ + unsigned char * newptr=ptr; + int S; ptr+=4; + for (S=4;;){ + size_t L=ptr[1]*256+ptr[0]; + if (L==0) break; + int l=strlen((const char *)ptr+2); + // filter py records + if (l>3 && strncmp((const char *)ptr+2+l-3,".py",3)==0){ + // if (ptr>newptr+S) + memmove(newptr+S,ptr,L); + S+=L; + } +#if 0 // def STRING_STORAGE + if (l>5 && strncmp((const char *)ptr+2+l-5,".func",5)==0){ + int shift=l+4+13; + Ion::Storage::Record * record=(Ion::Storage::Record *)malloc(1024); + memcpy(record,ptr,L); + //ExpressionModelHandle + Poincare::Expression e=Poincare::Expression::Parse((const char *)ptr+shift,NULL); + //ExpressionModel::setContent(Ion::Storage::Record * record, const char * c, Context * context, CodePoint symbol); + Shared::ExpressionModel md; + Ion::Storage::Record::ErrorStatus err=md.setExpressionContent(record, e); + if (1){ + // if (ptr>newptr+S) + int newL=record->value().size; + memmove(newptr+S,record,newL); + S+=newL; + } + free(record); + } +#endif + ptr+=L; + } +} + + +#ifdef NSPIRE_NEWLIB +#include "../../ion/src/simulator/fxcg/platform.h" +#include "../../ion/src/simulator/fxcg/menuHandler.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../../ion/src/simulator/nspire/k_csdk.h" +#include "../calculation/calculation_store.h" + +#define C_WHITE SDK_WHITE +#define C_BLACK SDK_BLACK +#define C_RED (31<<11) + +int do_getkey(){ + os_wait_1ms(50); + return getkey(0); +} + +void dtext(int x,int y,int fg,const char * s){ + os_draw_string_medium(x,y,fg,SDK_WHITE,s); +} + +void dclear(int c){ + os_fill_rect(0,0,LCD_WIDTH_PX,LCD_HEIGHT_PX,c); +} + +void dupdate(){ + sync_screen(); +} + +const int storage_length=60000; // 60000 in Upsilon, 32768 in Epsilon +// k_storageSize = 60000; in ion/include/ion/internal_storage.h +extern void * last_calculation_history; + +int load_state(const char * fname){ + FILE * f=fopen(fname,"rb"); + if (f){ + unsigned char * ptr=(unsigned char *)storage_address(); + fread(ptr,1,storage_length,f); + fclose(f); +#ifdef FILTER_STORE + filter(ptr); +#endif + return 1; + } + return 0; +} + +int save_state(const char * fname){ + save_calc_history(); + if (1 || Ion::Storage::sharedStorage()->numberOfRecords()){ + const unsigned char * ptr=(const unsigned char *)storage_address(); + // find store size + int S=4; + for (ptr+=4;;){ // skip header + size_t L=ptr[1]*256+ptr[0]; + ptr+=L; + S+=L; + if (L==0) break; + } + S = ((S+1023)/1024)*1024; +#ifdef FILTER_STORE + // keep only python scripts + unsigned char * newptr=(unsigned char *) malloc(S); + bzero(newptr,S); + ptr=(const unsigned char *) storage_address(); + memcpy(newptr,ptr,4); ptr+=4; + for (S=4;;){ + size_t L=ptr[1]*256+ptr[0]; + if (L==0) break; + int l=strlen((const char *)ptr+2); + if (l>3 && strncmp((const char *)ptr+2+l-3,".py",3)==0){ + memcpy(newptr+S,ptr,L); + S+=L; + } + ptr+=L; + } + S = ((S+1023)/1024)*1024; + FILE * f; + f=fopen(fname,"wb"); + if (f){ + fwrite(newptr,S,1,f); + //fwrite(ptr+4,1,S-4,f); + fclose(f); + free(newptr); + return S; + } + free(newptr); + return 0; +#else + ptr=(const unsigned char *)storage_address(); + FILE * f; + f=fopen(fname,"wb"); + if (f){ + fwrite(ptr,S,1,f); + //fwrite(ptr+4,1,S-4,f); + fclose(f); + return S; + } + return 0; +#endif + } + return 2; +} + +#endif // NSPIRE_NEWLIB + +#ifdef _FXCG +#include "../../ion/src/simulator/fxcg/platform.h" +#include "../../ion/src/simulator/fxcg/menuHandler.h" +#include "../calculation/calculation_store.h" +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#define KEY_CTRL_OK KEY_EXE +int do_getkey(){ + return getkey().key; +} + +const int storage_length=60000; // 60000 in Upsilon, 32768 in Epsilon +// k_storageSize = 60000; in ion/include/ion/internal_storage.h +extern void * last_calculation_history; + +int load_state(const char * fname){ + FILE * f=fopen(fname,"rb"); + if (f){ + unsigned char * ptr=(unsigned char *)storage_address(); + ptr[3]=fgetc(f); + ptr[2]=fgetc(f); + ptr[1]=fgetc(f); + ptr[0]=fgetc(f); + fread(ptr+4,1,storage_length-4,f); + fclose(f); + return 1; + } + return 0; +} + +int save_state(const char * fname){ + save_calc_history(); + if (Ion::Storage::sharedStorage()->numberOfRecords()){ +#if 0 + unsigned short pFile[512]; + convert(fname,pFile); + int hf = BFile_Open(pFile, BFile_WriteOnly); // Get handle + // cout << hf << endl << "f:" << filename << endl; Console_Disp(); + if (hf<0){ + int l=storage_length; + BFile_Create(pFile,0,&l); + hf = BFile_Open(pFile, BFile_WriteOnly); + } + if (hf < 0) + return 0; + int l=BFile_Write(hf,storage_address(),storage_length); + BFile_Close(hf); + if (l==storage_length) + return 1; + return -1; +#else + const unsigned char * ptr=(const unsigned char *)storage_address(); + // find store size + int S=4; + for (ptr+=4;;){ // skip header + size_t L=ptr[1]*256+ptr[0]; + ptr+=L; + S+=L; + if (L==0) break; + } + S = ((S+1023)/1024)*1024; + FILE * f=fopen(fname,"wb"); + if (f){ + ptr=(const unsigned char *) storage_address(); + fputc(ptr[3],f); + fputc(ptr[2],f); + fputc(ptr[1],f); + fputc(ptr[0],f); + //fwrite(ptr+4,1,S-4,f); + fwrite(ptr+4,S-4,1,f); + fclose(f); + return S; + } + return 0; +#endif + } + return 2; +} +#endif // _FXCG + +const char * retrieve_calc_history(){ +#if defined _FXCG || defined NSPIRE_NEWLIB + static bool firstrun=true; + if (firstrun){ +#ifdef _FXCG + int l=gint_world_switch(GINT_CALL(load_state,storage_name)); +#else + int l=load_state(storage_name); +#endif + if (l==0){ + display_host_help(); + // ((App*)m_app)->redraw(); + } + firstrun=false; + } +#endif + unsigned char * ptr=(unsigned char *)storage_address(); + for (ptr+=4;;){ // skip header + size_t L=ptr[1]*256+ptr[0]; + if (L==0) return 0; + if (strcmp((const char *)ptr+2,calc_storage_name)==0){ + const char * buf=(const char *)ptr+2+strlen(calc_storage_name)+1; + return buf; + } + ptr += L; + } + return 0; +} + +#if defined _FXCG || defined NSPIRE_NEWLIB +bool save_calc_history(){ + if (!last_calculation_history) + return false; + erase_record(calc_storage_name); + std::string s; + Calculation::CalculationStore * store=(Calculation::CalculationStore *) last_calculation_history; + int N=store->numberOfCalculations(); + for (int i=N-1;i>=0;--i){ + s += store->calculationAtIndex(i)->inputText(); + s += '\n'; + } + if (s.empty()) + return false; + Ion::Storage::Record::ErrorStatus res= Ion::Storage::sharedStorage()->createRecordWithFullName(calc_storage_name,&s[0],s.size()); + if (res==Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable) + return false; + return true; +} + +void confirm_load_state(const char * buf){ + dclear(C_WHITE); + dtext(1,1, C_BLACK, "Loading from state file"); + dtext(1,17,C_BLACK,buf); + dtext(1,33,C_BLACK,"Current context will be lost!"); + dtext(1,49,C_BLACK,"Press EXE to confirm"); + dupdate(); + int k=do_getkey(); + if (k==KEY_EXE || k==KEY_CTRL_OK){ +#ifdef _FXCG + int l=gint_world_switch(GINT_CALL(load_state,buf)); +#else + int l=load_state(buf); +#endif + char buf2[]="0"; + buf2[0] += l; + if (l==0) + dtext(1,65,C_BLACK,"Error reading state"); + if (l==1) + dtext(1,65,C_BLACK,"Success reading state"); + dtext(1,81,C_BLACK,buf2); + dtext(1,97,C_BLACK,"Press any key"); + dupdate(); + do_getkey(); + } +} + +static void convert(const char * fname,unsigned short * pFile){ + for ( ;*fname;++fname,++pFile) + *pFile=*fname; + *pFile=0; +} + +struct file { + std::string s; + int length; + bool isdir; +}; + +void host_scripts(std::vector & v,const char * dirname,const char * extension){ + v.clear(); + file f={".._parent_dir",0,true}; + if (strlen(dirname)>1) + v.push_back(f); + DIR *dp; + struct dirent *ep; + dp = opendir (dirname); + int l=extension?strlen(extension):0; + if (dp != NULL){ + int t; + while ( (ep = readdir (dp)) ){ + if (strlen(ep->d_name)>=1 && ep->d_name[0]=='.') + continue; + f.s=ep->d_name; + if (f.s=="@MainMem") + continue; +#ifdef NSPIRE_NEWLIB + DIR * chk=opendir((dirname+f.s).c_str()); + f.isdir=true; + if (chk) + closedir(chk); + else + f.isdir=false; +#else + f.isdir=ep->d_type==DT_DIR; +#endif +#if 1 + if (f.isdir) + f.length=0; + else { + struct stat st; + stat((dirname+f.s).c_str(), &st); + f.length = st.st_size; + if (f.length>=32768) + continue; + } +#else + f.length=f.isdir?0:-1; +#endif + if (f.isdir || !extension) + v.push_back(f); + else { + t=strlen(ep->d_name); + if (t>l && strncmp(ep->d_name+t-l,extension,l)==0) + v.push_back(f); + } + } + closedir (dp); + } +} + +void nw_scripts(std::vector & v,const char * extension){ + v.clear(); +#if 0 + int n=Ion::Storage::sharedStorage()->numberOfRecords(); + for (int i=0;irecordAtIndex(i).fullName()); + } +#else + const unsigned char * ptr=(const unsigned char *)storage_address(); + int l=extension?strlen(extension):0; + for (ptr+=4;;){ // skip header + size_t L=ptr[1]*256+ptr[0]; + ptr+=2; + if (L==0) break; + L-=2; + file f={(const char *)ptr,(int)L,false}; + if (!extension) + v.push_back(f); + else { + int namesize=strlen((const char *)ptr); + if (namesize>l && strncmp((const char *)ptr+namesize-l,extension,l)==0) + v.push_back(f); + } + ptr+=L; + } +#endif +} + +int copy_nw_to_host(const char * nwname,const char * hostname){ +#ifdef NSPIRE_NEWLIB + int s=strlen(hostname); + if (s<4 || strncmp(hostname+s-4,".tns",4)){ + std::string S(hostname); + S+=".tns"; + return copy_nw_to_host(nwname,S.c_str()); + } +#endif + const unsigned char * ptr=(const unsigned char *)storage_address(); + for (ptr+=4;;){ // skip header + size_t L=ptr[1]*256+ptr[0]; + if (L==0) return 3; // not found + //dclear(C_WHITE); + //dtext(1,1,C_BLACK,ptr+2); + //dtext(1,17,C_BLACK,nwname); + //dupdate(); + //getkey(); + if (strcmp((const char *)ptr+2,nwname)){ + ptr += L; + continue; + } + ptr+=2; + L-=2; + int l=strlen((const char *)ptr); + ptr += l+2; + L -= l; + L = 2*((L+1)/2); + FILE * f=fopen(hostname,"wb"); + if (!f) + return 2; + fwrite(ptr,1,L,f); + fclose(f); + return 0; + } + return 1; +} + +int copy_host_to_nw(const char * hostname,const char * nwname,int autoexec){ + FILE * f=fopen(hostname,"rb"); + if (!f) + return -1; + std::vector v(1,autoexec?1:0); + for (;;){ + unsigned char c=fgetc(f); + if (feof(f)){ + if (c>=' ' && c<=0x7e) + v.push_back(c); + break; + } + if (c==0xa && !v.empty() && v.back()==0xd) + v.back()=0xa; + else + v.push_back(c); + } + if (!v.empty() && v.back()!=0xa) + v.push_back(0xa); + v.push_back(0); + fclose(f); + if (Ion::Storage::sharedStorage()->hasRecord(Ion::Storage::sharedStorage()->recordNamed(nwname))) + Ion::Storage::sharedStorage()-> destroyRecord(Ion::Storage::sharedStorage()->recordNamed(nwname)); + Ion::Storage::Record::ErrorStatus res= Ion::Storage::sharedStorage()->createRecordWithFullName(nwname,&v.front(),v.size()); + if (res==Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable) + return -2; + return 0; +} + +bool filesort(const file & a,const file & b){ + if (a.isdir!=b.isdir) + return a.isdir; + return a.s v,w; +#ifdef NSPIRE_NEWLIB + std::string hostdir="/documents/"; +#else + std::string hostdir="/"; +#endif + bool onlypy=true; + for (;;){ + if (reload){ + nw_scripts(v,onlypy?".py":0); + sort(v.begin(),v.end(),filesort); +#ifdef NSPIRE_NEWLIB + host_scripts(w,hostdir.c_str(),onlypy?".py.tns":0); +#else + host_scripts(w,hostdir.c_str(),onlypy?".py":0); +#endif + sort(w.begin(),w.end(),filesort); + reload=false; + } + dclear(C_WHITE); + dtext(1,1, C_BLACK,"EXIT: leave; key 1 to 9: load state from file"); +#ifdef _FXCG + dtext(1,17,C_BLACK,"Cursor keys: move, /: rootdir, OPTN: all/py files"); +#else + dtext(1,17,C_BLACK,"Cursor keys: move, /: rootdir"); +#endif + dtext(1,33,C_BLACK,"EXE or STO key: copy selection from/to host"); + dtext(1,49,C_BLACK,("Upsilon records Host "+hostdir).c_str()); + int nitems=9; + if (posnw<0) + posnw=v.size()-1; + if (posnw>=int(v.size())) + posnw=0; + if (posnwstartnw+nitems) + startnw=posnw-4; + if (startnw>=int(v.size())-nitems) + startnw=v.size()-nitems; + if (startnw<0) + startnw=0; + if (v.empty()) + nw=false; + for (int i=0;i<=nitems;++i){ + int I=i+startnw; + if (I>=int(v.size())) + break; + dtext(1,65+16*i,(nw && I==posnw)?C_RED:C_BLACK,v[I].s.c_str()); + char buf[256]; + sprintf(buf,"%i",v[I].length); + dtext(90,65+16*i,(nw && I==posnw)?C_RED:C_BLACK,buf); + } + if (w.empty()) + nw=true; + if (poshost<0) + poshost=w.size()-1; + if (poshost>=int(w.size())) + poshost=0; + if (poshoststarthost+nitems) + starthost=poshost-4; + if (starthost>=int(w.size())-nitems) + starthost=w.size()-nitems; + if (starthost<0) + starthost=0; + for (int i=0;i<=nitems;++i){ + int I=i+starthost; + if (I>=int(w.size())) + break; + std::string fname=w[I].s; + if (fname.size()>16) + fname=fname.substr(0,16)+"..."; + dtext(192,65+16*i,(!nw && I==poshost)?C_RED:C_BLACK,fname.c_str()); + if (w[I].isdir) + dtext(154,65+16*i,(!nw && I==poshost)?C_RED:C_BLACK,""); + else { + char buf[256]; + sprintf(buf,"%i",w[I].length); +#ifdef _FXCG + dtext(340,65+16*i,(!nw && I==poshost)?C_RED:C_BLACK,buf); +#else + dtext(285,65+16*i,(!nw && I==poshost)?C_RED:C_BLACK,buf); +#endif + } + } + dupdate(); + int key=do_getkey(); + if (key==KEY_EXIT || key==KEY_MENU) + break; + if (key==KEY_OPTN || key=='\t'){ + onlypy=!onlypy; + reload=true; + continue; + } + if (key==KEY_DIV){ +#ifdef NSPIRE_NEWLIB + hostdir="/documents/"; +#else + hostdir="/"; +#endif + reload=true; + continue; + } + if (key==KEY_DEL){ + if (!nw && w[poshost].isdir) // can not remove directory + continue; + dclear(C_WHITE); + dtext(1,17,C_BLACK,nw?"About to suppress Upsilon record:":"About to suppress Host file:"); + dtext(1,33,C_BLACK,(nw?v[posnw].s:w[poshost].s).c_str()); + dtext(1,49,C_BLACK,"Press EXE or OK to confirm"); + dupdate(); + int ev=do_getkey(); + if (ev!=KEY_EXE && ev!=KEY_CTRL_OK) + continue; + if (nw){ +#if 1 + erase_record(v[posnw].s.c_str()); +#else + char buf[256]; + strcpy(buf,v[posnw].s.c_str()); + int l=strlen(buf)-4; + buf[l]=0; + Ion::Storage::sharedStorage()-> destroyRecordWithBaseNameAndExtension(buf,buf+l+1); +#endif + } + else + remove((hostdir+w[poshost].s).c_str()); + reload=true; + } + if (key==KEY_LEFT){ + nw=true; + continue; + } + if (key==KEY_RIGHT){ + nw=false; + continue; + } + if (key==KEY_PLUS){ + if (nw) + posnw+=5; + else + poshost+=5; + continue; + } + if (key==KEY_MINUS){ + if (nw) + posnw-=5; + else + poshost-=5; + continue; + } + if (key==KEY_DOWN){ + if (nw) + ++posnw; + else + ++poshost; + continue; + } + if (key==KEY_UP){ + if (nw) + --posnw; + else + --poshost; + continue; + } + int autoexec = key==KEY_EXE || key==KEY_CTRL_OK; + if (key==KEY_STORE || autoexec){ + if (nw && posnw>=0 && posnw=0 && poshost=0;--j){ + if (hostdir[j]=='/'){ + hostdir=hostdir.substr(0,j+1); + break; + } + } + reload=true; + continue; + } + // lookup if poshost is in directories + if (w[poshost].isdir){ + hostdir += w[poshost].s; + hostdir += "/"; + reload=true; + continue; + } + size_t i; + std::string fname=w[poshost].s; +#ifdef NSPIRE_NEWLIB + if (fname.size()>4 && fname.substr(fname.size()-4,4)==".tns") + fname=fname.substr(0,fname.size()-4); +#endif + for (i=0;i12){ + dclear(C_WHITE); + dtext(1,33,C_BLACK,"Host filename too long"); + dtext(1,49,C_BLACK,fname.c_str()); + dupdate(); + do_getkey(); + continue; + } + if (fname.size()>4 && fname.substr(fname.size()-4,4)==".nws") + confirm_load_state((hostdir+fname).c_str()); + else { +#ifdef _FXCG + gint_world_switch(GINT_CALL(copy_host_to_nw,(hostdir+fname).c_str(),nwname.c_str(),autoexec)); +#else + copy_host_to_nw((hostdir+w[poshost].s).c_str(),nwname.c_str(),autoexec); +#endif + } + reload=true; + } + } + if (key>=KEY_1 && key<=KEY_9){ +#ifdef NSPIRE_NEWLIB + char buf[]="nwstate0.nws.tns"; +#else + char buf[]="nwstate0.nws"; +#endif + buf[7]='1'+(key-KEY_1); + confirm_load_state(buf); + reload=true; + } + } +} +#endif // FXCG || NSPIRE \ No newline at end of file diff --git a/apps/shared/expression_model.cpp b/apps/shared/expression_model.cpp index ab7dd0d0933..d37e6b4cada 100644 --- a/apps/shared/expression_model.cpp +++ b/apps/shared/expression_model.cpp @@ -72,7 +72,7 @@ Expression ExpressionModel::expressionReduced(const Storage::Record * record, Po if (isCircularlyDefined(record, context)) { m_expression = Undefined::Builder(); } else { - m_expression = Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record)); + m_expression = Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record), record); /* 'Simplify' routine might need to call expressionReduced on the very * same function. So we need to keep a valid m_expression while executing * 'Simplify'. Thus, we use a temporary expression. */ @@ -90,7 +90,7 @@ Expression ExpressionModel::expressionReduced(const Storage::Record * record, Po Expression ExpressionModel::expressionClone(const Storage::Record * record) const { assert(record->fullName() != nullptr); /* A new Expression has to be created at each call (because it might be tempered with after calling) */ - return Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record)); + return Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record), record); /* TODO * The substitution of UCodePointUnknown back and forth is done in the * methods text, setContent (through BuildExpressionFromText), layout and @@ -125,6 +125,39 @@ Ion::Storage::Record::ErrorStatus ExpressionModel::setExpressionContent(Ion::Sto Ion::Storage::Record::Data newData = record->value(); size_t previousExpressionSize = expressionSize(record); size_t newExpressionSize = newExpression.isUninitialized() ? 0 : newExpression.size(); +#ifdef STRING_STORAGE + size_t stringsize = 0; + char buf[1024] = {0}; + char repl = 0; + buf[0] = '"'; + if (!newExpression.isUninitialized()) { + size_t l = newExpression.serialize(buf+1,sizeof(buf)-2); + if (l >= sizeof(buf) - 3) { + newExpressionSize = 0; + } else { + buf[l + 1] = '"'; + // replace 0x1 by x for func or n for seq + const char * name = record->fullName(); + int namel = strlen(name); + if (namel > 4 && strncmp(name + namel - 4, ".seq", 4) == 0) { + repl = 'n'; + } else if (namel > 5 && strncmp(name + namel - 5, ".func", 5) == 0) { + repl = 'x'; + } + if (repl) { + for (char * ptr = buf; *ptr; ++ptr) { + if (*ptr == 1) { + *ptr = repl; + } + } + } + stringsize= l + 3; // 2 quotes and 0 at end + if (newExpressionSize < stringsize) { + newExpressionSize = stringsize; + } + } + } +#endif size_t previousDataSize = newData.size; size_t newDataSize = previousDataSize - previousExpressionSize + newExpressionSize; void * expAddress = expressionAddress(record); @@ -140,6 +173,12 @@ Ion::Storage::Record::ErrorStatus ExpressionModel::setExpressionContent(Ion::Sto * (as it is sometimes computed from metadata). Thus, the expression address * is given as a parameter to updateNewDataWithExpression. */ updateNewDataWithExpression(record, newExpression, expAddress, newExpressionSize, previousExpressionSize); +#ifdef STRING_STORAGE + if (stringsize && stringsizesetValue(newData); diff --git a/build/platform.simulator.nspire.mak b/build/platform.simulator.nspire.mak new file mode 100644 index 00000000000..d158bbc09c9 --- /dev/null +++ b/build/platform.simulator.nspire.mak @@ -0,0 +1,11 @@ +TOOLCHAIN = nspire-gcc +EXE = elf + +EPSILON_TELEMETRY ?= 0 + +HANDY_TARGETS_EXTENSIONS = tns + +USE_LIBA = 0 +POINCARE_TREE_LOG = 0 + +SFLAGS := $(filter-out -fPIE, $(SFLAGS)) diff --git a/build/targets.simulator.nspire.mak b/build/targets.simulator.nspire.mak new file mode 100644 index 00000000000..574bb4d24ef --- /dev/null +++ b/build/targets.simulator.nspire.mak @@ -0,0 +1,6 @@ +$(BUILD_DIR)/%.tns: $(BUILD_DIR)/%.elf +# comment one of these lines. For B&W old nspire, do not use the compress option + genzehn --compress --input $(BUILD_DIR)/epsilon.elf --output upsilon.tns --name "upsilon" --uses-lcd-blit true +# genzehn --input $(BUILD_DIR)/epsilon.elf --output upsilon.tns --name "upsilon" --uses-lcd-blit true + genzehn --info --input upsilon.tns + firebird-send upsilon.tns /ndless diff --git a/build/toolchain.nspire-gcc.mak b/build/toolchain.nspire-gcc.mak new file mode 100644 index 00000000000..fe8518942d9 --- /dev/null +++ b/build/toolchain.nspire-gcc.mak @@ -0,0 +1,9 @@ +CC = nspire-gcc +CXX = nspire-g++ +LD = nspire-g++ +GDB = gdb +OBJCOPY = nspire-objcopy +SIZE = nspire-size +AS = nspire-as + +SFLAGS += -DNSPIRE_NEWLIB -DSTRING_STORAGE diff --git a/build/toolchain.sh-elf-gcc.mak b/build/toolchain.sh-elf-gcc.mak index 54290ffcec9..43432065c1b 100644 --- a/build/toolchain.sh-elf-gcc.mak +++ b/build/toolchain.sh-elf-gcc.mak @@ -7,4 +7,4 @@ SIZE = sh-elf-size AS = sh-elf-as FXGXA = fxgxa -SFLAGS += -D_FXCG -D_BIG_ENDIAN +SFLAGS += -D_FXCG -D_BIG_ENDIAN -DSTRING_STORAGE diff --git a/ion/include/ion/internal_storage.h b/ion/include/ion/internal_storage.h index fcad00c90d8..026ae5dc04d 100644 --- a/ion/include/ion/internal_storage.h +++ b/ion/include/ion/internal_storage.h @@ -22,8 +22,11 @@ class InternalStorage { static constexpr char expExtension[] = "exp"; static constexpr char funcExtension[] = "func"; static constexpr char seqExtension[] = "seq"; - +#ifdef _FXCG + constexpr static size_t k_storageSize = 65500; +#else constexpr static size_t k_storageSize = 60000; +#endif static_assert(UINT16_MAX >= k_storageSize, "record_size_t not big enough"); constexpr static char k_dotChar = '.'; @@ -195,7 +198,7 @@ class StorageDelegate { class StorageHelper { public: static uint16_t unalignedShort(char * address) { -#if (defined __EMSCRIPTEN__) || (defined _FXCG) +#if (defined __EMSCRIPTEN__) || (defined _FXCG) || defined NSPIRE_NEWLIB uint8_t f1 = *(address); uint8_t f2 = *(address+1); uint16_t f = (uint16_t)f1 + (((uint16_t)f2)<<8); @@ -205,7 +208,7 @@ class StorageHelper { #endif } static void writeUnalignedShort(uint16_t value, char * address) { -#if (defined __EMSCRIPTEN__) || (defined _FXCG) +#if (defined __EMSCRIPTEN__) || (defined _FXCG) || defined NSPIRE_NEWLIB *((uint8_t *)address) = (uint8_t)(value & ((1 << 8) - 1)); *((uint8_t *)address+1) = (uint8_t)(value >> 8); #else diff --git a/ion/src/simulator/external/config.nspire.mak b/ion/src/simulator/external/config.nspire.mak new file mode 100644 index 00000000000..5c9e873ab47 --- /dev/null +++ b/ion/src/simulator/external/config.nspire.mak @@ -0,0 +1,2 @@ +undefine sdl_src +undefine ion_simulator_sdl_src diff --git a/ion/src/simulator/fxcg/keyboard.cpp b/ion/src/simulator/fxcg/keyboard.cpp index 735b72e1f5c..12fc46ccc7d 100644 --- a/ion/src/simulator/fxcg/keyboard.cpp +++ b/ion/src/simulator/fxcg/keyboard.cpp @@ -221,11 +221,11 @@ State scan() { if (menuHeldFor > 30) { Simulator::FXCGMenuHandler::openMenu(); dupdate(); - // Wait until EXE is released + // Wait until EXE and MENU are released do { sleep_ms(10); clearevents(); - } while (keydown(KEY_EXE)); + } while (keydown(KEY_EXE) || keydown(KEY_MENU)); } } else { menuHeldFor = 0; @@ -259,4 +259,4 @@ namespace Keyboard { } } -} \ No newline at end of file +} diff --git a/ion/src/simulator/fxcg/main.cpp b/ion/src/simulator/fxcg/main.cpp index 9280dbc1253..3add310d4fb 100644 --- a/ion/src/simulator/fxcg/main.cpp +++ b/ion/src/simulator/fxcg/main.cpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include @@ -13,15 +15,60 @@ #include #include +#include "menuHandler.h" + +using namespace Ion::Simulator::Main; + +constexpr static const char * storage_name="nwstore.nws"; + +int save_state(const char * fname); // apps/home/controller.cpp extern "C" { -int main() { - Ion::Simulator::Main::init(); - ion_main(0, NULL); - Ion::Simulator::Main::quit(); + extern const int prizm_heap_size; + const int prizm_heap_size = 192 * 1024; + __attribute__((aligned(4))) char prizm_heap[prizm_heap_size]; + + CalculatorType calculator = CalculatorType::Unchecked; + + int main() { + /* Allow the user to use memory past the 2 MB line on tested OS versions */ + char const * os_version = (char const *)0x80020020; + if (!strncmp(os_version, "03.", 3) && os_version[3] <= '8'){ // 3.80 or earlier + char buf[256]; + strncpy(buf,os_version,8); + buf[8]=0; + } else { + char buf1[10], buf[256]; + // sprintf(buf,"%i",availram); + strncpy(buf1,os_version,8); + buf1[8]=0; + sprintf(buf,"OS %s not checked",buf1); + calculator = CalculatorType::Unchecked; + dclear(C_WHITE); + dtext(1,10,C_BLACK,buf); + dtext(1,27,C_BLACK,"F6: continue anyway"); + dupdate(); + int key = getkey().key; + if (key != KEY_F6){ + Ion::Simulator::FXCGMenuHandler::openMenu(); + } + } + bool is_emulator = *(volatile uint32_t *)0xff000044 == 0x00000000; + uint32_t stack; + __asm__("mov r15, %0" : "=r"(stack)); + // Check if the calculator is a Prizm or an emulator + if (stack < 0x8c000000) { + calculator = is_emulator ? CalculatorType::Emulator : CalculatorType::Other; + } else { + calculator = CalculatorType::Physical; // 0x8c200000, size 0x300000 is free + } - return 0; -} + Ion::Simulator::Main::init(); + ion_main(0, nullptr); + Ion::Simulator::Main::quit(); + + return 0; + } } namespace Ion { @@ -53,7 +100,7 @@ void quit() { Ion::Simulator::Display::quit(); } -void EnableStatusArea(int opt) { +void EnableStatusArea(int /*opt*/) { __asm__ __volatile__ ( ".align 2 \n\t" "mov.l 2f, r2 \n\t" @@ -106,6 +153,9 @@ void worldSwitchHandler(void (*worldSwitchFunction)(), bool prepareVRAM) { } void runPowerOffSafe(void (*powerOffSafeFunction)(), bool prepareVRAM) { + + gint_world_switch(GINT_CALL(save_state,storage_name)); + gint_world_switch(GINT_CALL(worldSwitchHandler, powerOffSafeFunction, prepareVRAM)); } diff --git a/ion/src/simulator/fxcg/main.h b/ion/src/simulator/fxcg/main.h index 4ac441a065f..41807fae825 100644 --- a/ion/src/simulator/fxcg/main.h +++ b/ion/src/simulator/fxcg/main.h @@ -1,5 +1,6 @@ #ifndef ION_SIMULATOR_MAIN_H #define ION_SIMULATOR_MAIN_H +#include namespace Ion { namespace Simulator { @@ -13,6 +14,14 @@ void refresh(); void runPowerOffSafe(void (*powerOffSafeFunction)(), bool prepareVRAM); +enum class CalculatorType : uint8_t { + Unchecked = (uint8_t)-1, + Unknown = 0, + Physical = 1, + Emulator = 2, + Other = 3 +}; + } } } diff --git a/ion/src/simulator/nspire/Makefile b/ion/src/simulator/nspire/Makefile new file mode 100644 index 00000000000..3d6d70f7ae9 --- /dev/null +++ b/ion/src/simulator/nspire/Makefile @@ -0,0 +1,53 @@ + +ion_src += $(addprefix ion/src/simulator/nspire/, \ + main.cpp \ + clipboard.cpp \ + display.cpp \ + framebuffer.cpp \ + telemetry_init.cpp \ + keyboard.cpp \ + events_keyboard.cpp \ + events.cpp \ + timing.cpp \ + console.cpp \ + backlight.cpp \ + power.cpp \ + k_csdk.c \ +) + +liba_src += $(addprefix liba/src/, \ + strlcat.c \ + strlcpy.c \ +) + +ion_src += ion/src/shared/collect_registers.cpp + +sdl_simu_needs_to_be_removed += $(addprefix ion/src/simulator/shared/, \ + main.cpp \ + clipboard.cpp \ + display.cpp \ + framebuffer.cpp \ + keyboard.cpp \ + events_keyboard.cpp \ + events_platform.cpp \ + events.cpp \ + layout.cpp \ + actions.cpp \ + window.cpp \ + timing.cpp \ + console.cpp \ +) + +sdl_simu_needs_to_be_removed += $(addprefix ion/src/shared/dummy/, \ + backlight.cpp \ + power.cpp \ +) + +# Remove the dummy diaplay (re-implemented) and the SDL simulator stuff. +ion_src := $(filter-out $(sdl_simu_needs_to_be_removed),$(ion_src)) + +SFLAGS := $(filter-out -Iion/src/simulator/external/sdl/include,$(SFLAGS)) + +SFLAGS += -marm -DNSPIRE_NEWLIB -I/Users/parisse/Ndless-r2015/ndless-sdk/ndless/include -I/Users/parisse/Ndless-r2015/ndless-sdk/toolchain/install/include -fno-strict-aliasing -I. -I.. -fno-exceptions -ffreestanding -nostdlib -fstrict-volatile-bitfields -g -Os +LDFLAGS += -Wl,--nspireio,--gc-sections -L/Users/parisse/Ndless-r2015/ndless-sdk/lib -L/Users/parisse/Ndless-r2015/ndless-sdk/toolchain/install/lib -nostdlib -lc -lm -lstdc++ -lgcc + diff --git a/ion/src/simulator/nspire/assets/icon-sel.png b/ion/src/simulator/nspire/assets/icon-sel.png new file mode 100644 index 00000000000..a016c36b39d Binary files /dev/null and b/ion/src/simulator/nspire/assets/icon-sel.png differ diff --git a/ion/src/simulator/nspire/assets/icon-uns.png b/ion/src/simulator/nspire/assets/icon-uns.png new file mode 100644 index 00000000000..c2f71847222 Binary files /dev/null and b/ion/src/simulator/nspire/assets/icon-uns.png differ diff --git a/ion/src/simulator/nspire/backlight.cpp b/ion/src/simulator/nspire/backlight.cpp new file mode 100644 index 00000000000..5f8575af972 --- /dev/null +++ b/ion/src/simulator/nspire/backlight.cpp @@ -0,0 +1,24 @@ +#include + +namespace Ion { +namespace Backlight { + +uint8_t brightness() { + return 128; +} + +void setBrightness(uint8_t b) { +} + +void init() { +} + +bool isInitialized() { + return true; +} + +void shutdown() { +} + +} +} diff --git a/ion/src/simulator/nspire/clipboard.cpp b/ion/src/simulator/nspire/clipboard.cpp new file mode 100644 index 00000000000..4bbee1fa382 --- /dev/null +++ b/ion/src/simulator/nspire/clipboard.cpp @@ -0,0 +1,15 @@ +#include +#include +#include + +namespace Ion { +namespace Clipboard { + +void write(const char * text) {} + +const char * read() { + return nullptr; +} + +} +} diff --git a/ion/src/simulator/nspire/console.cpp b/ion/src/simulator/nspire/console.cpp new file mode 100644 index 00000000000..d8957c6e6ca --- /dev/null +++ b/ion/src/simulator/nspire/console.cpp @@ -0,0 +1,24 @@ +#include +#include "main.h" +#include +#include + +namespace Ion { +namespace Console { + +char readChar() { + return 0; +} + +void writeChar(char c) { + // fxlibc conflicts with this + #undef putchar + KDIonContext::putchar(c); +} + +bool transmissionDone() { + return true; +} + +} +} diff --git a/ion/src/simulator/nspire/display.cpp b/ion/src/simulator/nspire/display.cpp new file mode 100644 index 00000000000..eff8185ed5d --- /dev/null +++ b/ion/src/simulator/nspire/display.cpp @@ -0,0 +1,47 @@ +#include "display.h" +#include "framebuffer.h" +#include +#include +#include +#include + +#include + +#include "k_csdk.h" +#include + +namespace Ion { +namespace Simulator { +namespace Display { + +void init() { +} + +void quit() { +} + +void draw() { + // copy framebuffer + const short unsigned int * ptr = (const short unsigned int *) Ion::Simulator::Framebuffer::address(); + Gc * gcptr = get_gc(); + for (int j = 0; j < LCD_HEIGHT_PX; ++j) { + for (int i = 0; i < LCD_WIDTH_PX;){ + int c = *ptr; + int k = 1; + for (; k+i < LCD_WIDTH_PX; ++k) { + if (ptr[k]!=c) { + break; + } + } + gui_gc_setColor(*gcptr,c_rgb565to888(c)); + gui_gc_drawRect(*gcptr,i,j,k-1,0); + ptr += k; + i += k; + } + } + sync_screen(); +} + +} +} +} diff --git a/ion/src/simulator/nspire/display.h b/ion/src/simulator/nspire/display.h new file mode 100644 index 00000000000..ab524fd3c55 --- /dev/null +++ b/ion/src/simulator/nspire/display.h @@ -0,0 +1,19 @@ +#ifndef ION_SIMULATOR_DISPLAY_H +#define ION_SIMULATOR_DISPLAY_H + +#include + +namespace Ion { +namespace Simulator { +namespace Display { + +void init(); +void quit(); + +void draw(); + +} +} +} + +#endif diff --git a/ion/src/simulator/nspire/events.cpp b/ion/src/simulator/nspire/events.cpp new file mode 100644 index 00000000000..9b8d0055751 --- /dev/null +++ b/ion/src/simulator/nspire/events.cpp @@ -0,0 +1,23 @@ +#include "events.h" +#include + +namespace Ion { +namespace Events { + +void didPressNewKey() { +} + +char * sharedExternalTextBuffer() { + static char buffer[sharedExternalTextBufferSize]; + return buffer; +} + +const char * Event::text() const { + if (*this == ExternalText) { + return const_cast(sharedExternalTextBuffer()); + } + return defaultText(); +} + +} +} diff --git a/ion/src/simulator/nspire/events.h b/ion/src/simulator/nspire/events.h new file mode 100644 index 00000000000..b012a43c5e5 --- /dev/null +++ b/ion/src/simulator/nspire/events.h @@ -0,0 +1,24 @@ +#ifndef ION_SIMULATOR_EVENTS_H +#define ION_SIMULATOR_EVENTS_H + +#include + +namespace Ion { +namespace Simulator { +namespace Events { + +void dumpEventCount(int i); +void logAfter(int numberOfEvents); + +} +} + +namespace Events { + +static constexpr int sharedExternalTextBufferSize = 2; +char * sharedExternalTextBuffer(); + +} +} + +#endif diff --git a/ion/src/simulator/nspire/events_keyboard.cpp b/ion/src/simulator/nspire/events_keyboard.cpp new file mode 100644 index 00000000000..615c327d54c --- /dev/null +++ b/ion/src/simulator/nspire/events_keyboard.cpp @@ -0,0 +1,15 @@ +#include + +namespace Ion { +namespace Events { + + +Event getPlatformEvent() { + Event result = None; + + return result; +} + + +} +} diff --git a/ion/src/simulator/nspire/framebuffer.cpp b/ion/src/simulator/nspire/framebuffer.cpp new file mode 100644 index 00000000000..677515ecf63 --- /dev/null +++ b/ion/src/simulator/nspire/framebuffer.cpp @@ -0,0 +1,53 @@ +#include "framebuffer.h" +#include +#include "main.h" +#include + + +static KDColor sPixels[Ion::Display::Width * Ion::Display::Height]; +static_assert(sizeof(KDColor) == sizeof(uint16_t), "KDColor is not 16 bits"); +static bool sFrameBufferActive = true; + +namespace Ion { +namespace Display { + +static KDFrameBuffer sFrameBuffer = KDFrameBuffer(sPixels, KDSize(Ion::Display::Width, Ion::Display::Height)); + +void pushRect(KDRect r, const KDColor * pixels) { + if (sFrameBufferActive) { + Simulator::Main::setNeedsRefresh(); + sFrameBuffer.pushRect(r, pixels); + } +} + +void pushRectUniform(KDRect r, KDColor c) { + if (sFrameBufferActive) { + Simulator::Main::setNeedsRefresh(); + sFrameBuffer.pushRectUniform(r, c); + } +} + +void pullRect(KDRect r, KDColor * pixels) { + if (sFrameBufferActive) { + sFrameBuffer.pullRect(r, pixels); + } +} + +} +} + +namespace Ion { +namespace Simulator { +namespace Framebuffer { + +const KDColor * address() { + return sPixels; +} + +void setActive(bool enabled) { + sFrameBufferActive = enabled; +} + +} +} +} diff --git a/ion/src/simulator/nspire/framebuffer.h b/ion/src/simulator/nspire/framebuffer.h new file mode 100644 index 00000000000..dba4dbd3278 --- /dev/null +++ b/ion/src/simulator/nspire/framebuffer.h @@ -0,0 +1,17 @@ +#ifndef ION_SIMULATOR_FRAMEBUFFER_H +#define ION_SIMULATOR_FRAMEBUFFER_H + +#include + +namespace Ion { +namespace Simulator { +namespace Framebuffer { + +const KDColor * address(); +void setActive(bool enabled); + +} +} +} + +#endif \ No newline at end of file diff --git a/ion/src/simulator/nspire/gint.h b/ion/src/simulator/nspire/gint.h new file mode 100644 index 00000000000..bd8d65a70f9 --- /dev/null +++ b/ion/src/simulator/nspire/gint.h @@ -0,0 +1,61 @@ +#ifndef GINT_H +#define GINT_H +#include "k_defs.h" +#define KEY_DOWN KEY_CTRL_DOWN +#define KEY_LEFT KEY_CTRL_LEFT +#define KEY_RIGHT KEY_CTRL_RIGHT +#define KEY_UP KEY_CTRL_UP +#define KEY_EXIT KEY_CTRL_EXIT +#define KEY_MENU KEY_CTRL_MENU +#define KEY_SHIFT KEY_CTRL_SHIFT +#define KEY_ALPHA KEY_CTRL_ALPHA +#define KEY_XOT KEY_CTRL_XTT +#define KEY_VARS KEY_CTRL_VARS +#define KEY_OPTN KEY_CTRL_OPTN +#define KEY_DEL KEY_CTRL_DEL +#define KEY_OPTN KEY_CTRL_OPTN +#define KEY_DEL KEY_CTRL_DEL +#define KEY_LN KEY_CHAR_LN +#define KEY_LOG KEY_CHAR_LOG +#define KEY_0 KEY_CHAR_0 +#define KEY_COMMA KEY_CHAR_COMMA +#define KEY_SIN KEY_CHAR_SIN +#define KEY_COS KEY_CHAR_COS +#define KEY_TAN KEY_CHAR_TAN +#define KEY_EXP KEY_CHAR_EXP +#define KEY_SQUARE KEY_CHAR_SQUARE +#define KEY_7 KEY_CHAR_7 +#define KEY_8 KEY_CHAR_8 +#define KEY_9 KEY_CHAR_9 +#define KEY_LEFTP KEY_CHAR_LPAR +#define KEY_RIGHTP KEY_CHAR_RPAR +#define KEY_4 KEY_CHAR_4 +#define KEY_5 KEY_CHAR_5 +#define KEY_6 KEY_CHAR_6 +#define KEY_MUL KEY_CHAR_MULT +#define KEY_DIV KEY_CHAR_DIV +#define KEY_FRAC KEY_CHAR_FRAC +#define KEY_1 KEY_CHAR_1 +#define KEY_2 KEY_CHAR_2 +#define KEY_3 KEY_CHAR_3 +#define KEY_ADD KEY_CHAR_PLUS +#define KEY_PLUS KEY_CHAR_PLUS +#define KEY_SUB KEY_CHAR_MINUS +#define KEY_MINUS KEY_CHAR_MINUS +#define KEY_NEG KEY_CHAR_PMINUS +#define KEY_0 KEY_CHAR_0 +#define KEY_DOT KEY_CHAR_DP +#define KEY_EXE KEY_CTRL_EXE +#define KEY_ACON KEY_CTRL_AC +#define KEY_POWER KEY_CHAR_POW +#define KEY_STORE KEY_CHAR_STORE +#define KEY_F1 KEY_CTRL_F1 +#define KEY_F2 KEY_CTRL_F2 +#define KEY_F3 KEY_CTRL_F3 +#define KEY_F4 KEY_CTRL_F4 +#define KEY_F5 KEY_CTRL_F5 +#define KEY_F6 KEY_CTRL_F6 +#define KEY_FD KEY_CTRL_FD +#define KEY_ARROW KEY_CTRL_SD +#define KEY_TIMES KEY_CHAR_MULT +#endif // GINT_H diff --git a/ion/src/simulator/nspire/k_csdk.c b/ion/src/simulator/nspire/k_csdk.c new file mode 100755 index 00000000000..4e9521f25ff --- /dev/null +++ b/ion/src/simulator/nspire/k_csdk.c @@ -0,0 +1,1556 @@ +// implementation of the minimal C SDK for KhiCAS +int (*shutdown)()=0; + +short shutdown_state=0; +short exam_mode=0,nspire_exam_mode=0; +unsigned exam_start=0; // RTC start +int exam_duration=0; +// <0: indicative duration, ==0 time displayed during exam, >0 end exam_mode after +const int exam_bg1=0x4321,exam_bg2=0x1234; +int exam_bg(){ + return exam_mode?(exam_duration>0?exam_bg1:exam_bg2):0x50719; +} + +void SetQuitHandler( void (*f)(void)){} +#ifdef TICE +int clip_ymin=0; +// TI83 +const int STATUS_AREA_PX=18; +// debug: dbg_printf() Add #include to a source file, and use make debug instead of make to build a debug program. You may need to run make clean beforehand in order to ensure all source files are rebuilt. +// ASM syscalls: https://wikiti.brandonw.net/index.php?title=Category:84PCE:Syscalls:By_Name +// doc: https://ce-programming.github.io/toolchain/index.html +// Makefile options https://ce-programming.github.io/toolchain/static/makefile-options.html +// memory layout: https://ce-programming.github.io/toolchain/static/faq.html +// parameters are in CEdev/meta (and app_tools if present) +// makefile.mk: +// BSSHEAP_LOW ?= D052C6 +// BSSHEAP_HIGH ?= D13FD8 +// STACK_HIGH ?= D1A87E +// INIT_LOC ?= D1A87F +// Can we set STACK_HIGH to another value? I think the global area stack+data could be "reversed", I mean stack top at 0xD2A87F and data(+code+ro_data for RAM programs) at a new position: INIT_LOC=D1987E (maybe +1 or +2) + +// TI stack 4K D1A87Eh: Top of the SPL stack. +// change stack pointer (if STACK_HIGH change does not work) +// requires assembly code (https://0x04.net/~mwk/doc/z80/eZ80.pdf), +// save stack pointer +// LD (Mmn), SP +// set HL to the new stack address (top of the area-3) +// LD SP,HL +// call main +// restore stack pointer +// LD SP,(Mmn) +// 1023 bytes: uint8_t[1023] os_RamCode (do not use if flash write occurs) +// 0xD052C6: 60989 bytes used for bss+heap (temp buffers in TI OS) +// 0xD1A881: Start of UserMem. 64K for code, data, ro data +// size_t os_MemChk(void **free) size and position of free ram area +// Or we could create a VarApp in RAM with no real data inside and use this area for temporary storage. +// 0xD40000: Start of VRAM. 320x240x2 bytes = 153600 bytes. +// half may be used in 8 bits palette mode (graphx) +#include "k_csdk.h" +#include +#include +#include +#include +#include +#include // boot_GetTime(uint8_t *seconds, uint8_t *minutes, uint8_t *hours), boot_SetTime(uint8_t seconds, uint8_t minutes, uint8_t hours) +#include +#include +#include +#include +#include +#define FILENAME_MAXRECORDS 32 +#define FILENAME_MAXSIZE 9 +#define FILE_MAXSIZE 16384 +char os_filenames[FILENAME_MAXRECORDS][FILENAME_MAXSIZE]; + +void sdk_init(){ + dbg_printf("SDK Init\n"); + gfx_Begin(); + unsigned short * addr=gfx_palette; + for (int r=0;r<4;r++){ + for (int g=0;g<8;g++){ + for (int b=0;b<4;b++){ + int R=r*255/3,G=g*255/7,B=b*255/3; + addr[(r<<5)|(g<<2)|b]=gfx_RGBTo1555(R,G,B); + // dbg_printf("palette %i %i %i %i\n",(r<<5)|(g<<2)|b,R,G,B); + } + } + } + // 128-254 arc-en-ciel? 255 should remain white +} + +void sdk_end(){ + dbg_printf("SDK End\n"); + gfx_End(); +} + +void clear_screen(void){ + gfx_FillScreen(255); // gfx_ZeroScreen(void); +} + +int alpha=0,alphalock=0,prevalpha=0,shift=0; +int handle_f5(){ + if (alphalock) + alphalock=3-alphalock; + else + alphalock=2; +} +void dbgprint(int i){ + char buf[16]={0}; + buf[0]='0'+i/100; + buf[1]='0'+(i % 100)/10; + buf[2]='0'+(i % 10); + os_draw_string(20,60,SDK_WHITE,SDK_BLACK,buf,false); +} +int getkey(int allow_suspend){ + sync_screen(); + statusline(0); + for (;;){ + int i=0; + while (!i){ + i=os_GetCSC(); + } + // dbgprint(i); + int decal=(alpha>>1)<<5; // 0 or 32 for upper or lowercase + int Alpha=alpha,Shift=shift; + shift=0; prevalpha=alpha; + if (!alphalock) + alpha=0; + switch (i){ + case sk_Fx: + return Alpha?KEY_CTRL_F11:Shift?KEY_CTRL_F6:KEY_CTRL_F1; + case sk_Fenetre: + return Alpha?KEY_CTRL_F12:Shift?KEY_CTRL_F7:KEY_CTRL_F2; + case sk_Zoom: + return Alpha?KEY_CTRL_F13:Shift?KEY_CTRL_F8:KEY_CTRL_F3; + case sk_Trace: + return Alpha?KEY_CTRL_F14:Shift?KEY_CTRL_F9:KEY_CTRL_F4; + case sk_Graph: + return Alpha?KEY_CTRL_F15:Shift?KEY_CTRL_F10:KEY_CTRL_F5; + case sk_Mode: + return KEY_CTRL_SETUP; + case sk_Del: + return KEY_CTRL_DEL; + case sk_GraphVar: + return KEY_CTRL_XTT; + // sk_Stats + case sk_Right: + return Shift?KEY_SHIFT_RIGHT:KEY_CTRL_RIGHT; + case sk_Left: + return Shift?KEY_SHIFT_LEFT:KEY_CTRL_LEFT; + case sk_Up: + return Shift?KEY_CTRL_PAGEUP:KEY_CTRL_UP; + case sk_Down: + return Shift?KEY_CTRL_PAGEDOWN:KEY_CTRL_DOWN; + case sk_Enter: + return Alpha?KEY_SHIFT_ANS:KEY_CTRL_EXE; + case sk_Alpha: + if (alphalock){ + alpha=alphalock=0; + } + else { + if (Shift) + alphalock=alpha=2; + else { + alpha=2; + if (prevalpha) + alphalock=alpha=prevalpha; + } + } + statusline(0); + continue; + case sk_2nd: + if (alphalock) + alpha=3-alpha; // maj <> min + else + shift=!Shift; + statusline(0); + continue; + case sk_Math: + return Alpha?KEY_CHAR_A+decal:KEY_CTRL_F6; + case sk_Matrice: + return Alpha?KEY_CHAR_B+decal:KEY_CHAR_MAT; + case sk_Prgm: + return Alpha?KEY_CHAR_C+decal:KEY_CTRL_PRGM; + case sk_Vars: + return KEY_CTRL_VARS; + case sk_Annul: + return Shift?KEY_CTRL_AC:KEY_CTRL_EXIT; + case sk_TglExact: + return KEY_CHAR_D+decal; + case sk_Trig: + return Alpha?KEY_CHAR_E+decal:(Shift?KEY_CHAR_PI:KEY_CHAR_SIN); + case sk_Cos: + return Alpha?KEY_CHAR_F+decal:KEY_CHAR_COS; + case sk_Tan: + return Alpha?KEY_CHAR_G+decal:KEY_CHAR_TAN; + case sk_Power: + return Alpha?KEY_CHAR_H+decal:KEY_CHAR_POW; + case sk_Square: + return Alpha?KEY_CHAR_I+decal:Shift?KEY_CHAR_ROOT:KEY_CHAR_SQUARE; + case sk_Comma: + return Alpha?KEY_CHAR_J+decal:Shift?KEY_CHAR_E:KEY_CHAR_COMMA; + case sk_LParen: + return Alpha?KEY_CHAR_K+decal:Shift?KEY_CHAR_LBRACE:KEY_CHAR_LPAR; + case sk_RParen: + return Alpha?KEY_CHAR_L+decal:Shift?KEY_CHAR_RBRACE:KEY_CHAR_RPAR; + case sk_Div: + return Alpha?KEY_CHAR_M+decal:Shift?KEY_CHAR_E+32:KEY_CHAR_DIV; + case sk_Log: + return Alpha?KEY_CHAR_N+decal:Shift?KEY_CHAR_EXPN10:KEY_CHAR_LOG; + case sk_7: + return Alpha?KEY_CHAR_O+decal:KEY_CHAR_7; + case sk_8: + return Alpha?KEY_CHAR_P+decal:KEY_CHAR_8; + case sk_9: + return Alpha?KEY_CHAR_Q+decal:KEY_CHAR_9; + case sk_Mul: + return Alpha?KEY_CHAR_R+decal:Shift?KEY_CHAR_LBRCKT:KEY_CHAR_MULT; + case sk_Ln: + return Alpha?KEY_CHAR_S+decal:Shift?KEY_CHAR_EXP:KEY_CHAR_LN; + case sk_4: + return Alpha?KEY_CHAR_T+decal:KEY_CHAR_4; + case sk_5: + return Alpha?KEY_CHAR_U+decal:KEY_CHAR_5; + case sk_6: + return Alpha?KEY_CHAR_V+decal:KEY_CHAR_6; + case sk_Sub: + return Alpha?KEY_CHAR_W+decal:Shift?KEY_CHAR_RBRCKT:KEY_CHAR_MINUS; + case sk_Store: + return Alpha?KEY_CHAR_X+decal:KEY_CHAR_STORE; + case sk_1: + return Alpha?KEY_CHAR_Y+decal:KEY_CHAR_1; + case sk_2: + return Alpha?KEY_CHAR_Z+decal:KEY_CHAR_2; + case sk_3: + return Alpha?KEY_CHAR_THETA:KEY_CHAR_3; + case sk_Add: + return KEY_CHAR_PLUS; + case sk_0: + return Alpha?KEY_CHAR_SPACE:Shift?KEY_CTRL_CATALOG:KEY_CHAR_0; + case sk_DecPnt: + return Alpha?':':Shift?KEY_CHAR_I+32:KEY_CHAR_DP; + case sk_Chs: + return Alpha?'?':Shift?KEY_CHAR_ANS:KEY_CHAR_PMINUS; + default: + return i; + } + } +} +void GetKey(int * key){ + *key=getkey(0); +} +int iskeydown(int key){ + kb_Scan(); + return kb_IsDown(key); +} + +// if (kb_On) ... +void enable_back_interrupt(){ + kb_EnableOnLatch(); +} +void disable_back_interrupt(){ + kb_DisableOnLatch(); +} +int isalphaactive(){ + return alpha; +} +int alphawasactive(int * key){ + return prevalpha; +} +void lock_alpha(){ + alpha=alphalock=1; +} +void reset_kbd(){ + shift=alpha=alphalock=0; +} +int GetSetupSetting(int k){ + if (k!=0x14) return -1; + if (!alpha) return 0; + if (!alphalock) return alpha==2?8:4; + return alpha==2?0x88:0x84; +} + +void os_wait_1ms(int ms){ + msleep(ms); // delay(ms)? +} +double millis(){ + return rtc_Days*86400.0+rtc_Hours*3600.+rtc_Minutes*60.+rtc_Seconds; +} +int os_set_angle_unit(int mode){ + if (mode) os_ResetFlag(TRIG,DEGREES); else os_SetFlag(TRIG,DEGREES); + return true; +} + +int os_get_angle_unit(){ + int i=os_TestFlag(TRIG,DEGREES); + return i?0:1; +} +int file_exists(const char * filename){ + int h=ti_Open(filename, "r"); + if (!h) + return false; + ti_Close(h); + return true; +} +int erase_file(const char * filename){ + if (!file_exists(filename)) + return false; + ti_Delete(filename); + return true; +} +const char * read_file(const char * filename){ + const char * ext=0; + int l=strlen(filename); + char var[9]={0}; + strncpy(var,filename,8); + for (--l;l>0;--l){ + if (filename[l]=='.'){ + ext=filename+l+1; + if (l<9) + var[l]=0; + break; + } + } + int h=ti_Open(var, "r"); + if (!h) + return 0; + int s=ti_GetSize(h); + if (s>7){ + //unsigned short u; + //ti_Read(&u,1,2,h); + char subtype[8]={0}; + ti_Read(subtype,1,4,h); + if (strncmp(subtype,"PYCD",4)==0 || strncmp(subtype,"XCAS",4)==0){ + unsigned char dx; + ti_Read(&dx,1,1,h); + if (dx!=0){ + // skip desktop filename + char buf[256]={0}; + ti_Read(buf,1,1,dx); + s -= 4+dx; + dbg_printf("subtype=%s filename=%s %i %i\n",subtype,buf,dx,s); + } + else + s -= 4; + } + else + ti_Seek(0,SEEK_SET,h); + } + char * ptr=0; +#if 0 + // Direct access to the data, ptr should not be used if any change to the TI variables occurs, unfortunately there is no 0 at end of string + ptr= ti_GetDataPtr(h); + ti_Close(h); + dbg_printf("data=%x %x %x %x %x %x %x %x\n",ptr[0],ptr[1],ptr[2],ptr[3],ptr[4],ptr[5],ptr[6],ptr[7]); + return ptr; +#endif + // Code requiring a copy + // if it starts with + // char * ptr=(char *) gfx_vram+LCD_WIDTH_PX*LCD_HEIGHT_PX; // pointer in vram buffer + int S=os_MemChk((void **)&ptr); + if (s>=S) + return 0; + S=ti_Read(ptr,1,s,h); + ptr[S]=0; + ti_Close(h); + dbg_printf("data=%s\n",ptr); + return ptr; +} +int write_file(const char * filename,const char * s,int len){ + // find extension + const char * ext=0; + int l=strlen(filename); + char var[9]={0}; + strncpy(var,filename,8); + for (--l;l>0;--l){ + if (filename[l]=='.'){ + ext=filename+l+1; + if (l<9) + var[l]=0; + break; + } + } + int h=ti_Open(var,"w"); + if (!h) return false; + if (ext){ + bool ispy=strncmp(ext,"py",2)==0; + bool isxw=strncmp(ext,"xw",2)==0; + if (ispy || isxw){ + const char * subtype=isxw?"XCAS":"PYCD"; + ti_Write(subtype,strlen(subtype),1,h); + unsigned char dx=strlen(filename)+1; + ti_Write(&dx,1,1,h); + ti_Write(filename,dx-1,1,h); + } + } + int Len=ti_Write(s,1,len,h); + ti_Close(h); + return Len==len; +} + +int os_file_browser(const char ** filenames,int maxrecords,const char * extension,int storage){ + if (maxrecords>FILENAME_MAXRECORDS) + maxrecords=FILENAME_MAXRECORDS; + void * ptr=os_GetSymTablePtr(); + int cur=0; + for (int count=0;cur=FILENAME_MAXSIZE || !dataptr) + continue; + s[l]=0; + dbg_printf("filebrowser %s %i %x %x %x %x %x %x %x %x %x %x %x %x %x\n",s,type,dataptr[0]&0xff,dataptr[1]&0xff,dataptr[2]&0xff,dataptr[3]&0xff,dataptr[4]&0xff,dataptr[5]&0xff,dataptr[6]&0xff,dataptr[7]&0xff,dataptr[8]&0xff,dataptr[9]&0xff,dataptr[10]&0xff,dataptr[11]&0xff,dataptr[12]&0xff); + // if type==21 dataptr[1]*256+dataptr[0]==size, then data + // xcas session begins with 4 bytes size, on the 83 should be 00 00 xx xx + if (type==21 && dataptr[2]==0 && dataptr[3]==0) + ext="xw"; + // python app, starts with 2 bytes size, "PYCD" or "PYSC" + // the script ifself begins at data.begin() + 6 + scriptOffset + // where scriptOffset = dataptr[6] + 1 + if (!ext){ + if (strncmp(&dataptr[2],"PYCD",4)==0 || strncmp(&dataptr[2],"PYSC",4)==0) + ext="py"; + else if (strncmp(&dataptr[2],"XCAS",4)==0) + ext="xw"; + else { // extension from filename _xw or _py or _... + //dbg_printf("os_file_browser %i %i %x\n",type,l,dataptr); + //dbg_printf("filename %i %s\n",count,s); + for (j=l-1;j>0;--j){ + if (s[j]=='_'){ + ext=s+j+1; + break; + } + } + } + } + if (ext && strcmp(ext,extension)==0){ + if (exam_mode && + (strcmp(s,"session")!=0 + ) + ) + continue; + strncpy(os_filenames[cur],s,FILENAME_MAXSIZE); + filenames[cur]=os_filenames[cur]; + dbg_printf("extension match %i %s %s\n",cur,s,filenames[cur]); + ++cur; + } + } + dbg_printf("filebrowser %i\n",cur); + return cur; +} +// gfx_Begin, gfx_SetDrawBuffer(); gfx_End +// GFX_LCD_WIDTH, HEIGHT, gfx_vbuffer=LCD RAM buffer 76800 bytes +// gfx_vram Total of 153600 bytes in size = 320x240x2 +// gfx_SetDrawBuffer()gfx_SetDrawScreen() +// uint8_t gfx_SetColor(uint8_t index) +// gfx_SetPixel(uint24_t x, uint8_t y) +// uint8_t gfx_GetPixel(uint24_t x, uint8_t y) +// gfx_FillRectangle(int x, int y, int width, int height) +// gfx_FillRectangle_NoClip(uint24_t x, uint8_t y, uint24_t width, uint8_t height) +// gfx_Wait(void) +// gfx_PrintStringXY(const char *string, int x, int y) +//gfx_SetTextFGColor(uint8_t color) +// gfx_SetTextScale(uint8_t width_scale, uint8_t height_scale) +// gfx_SetTextConfig +void sync_screen(){ + //gfx_Wait(); + // gfx_BlitBuffer(); // shoud be done if gfx_SetDrawBuffer() is active; +} +int c_rgb565to888(int c){ + c &= 0xffff; + int r=(c>>11)&0x1f,g=(c>>5)&0x3f,b=c&0x1f; + return (r<<19)|(g<<10)|(b<<3); +} + +int convertcolor(int c){ + // convert 16 bits to default palette + c &= 0xffff; + int r=(c>>11)&0x1f,g=(c>>5)&0x3f,b=c&0x1f; + int R = ((r>>3)<<5) | ((g>>3)<<2) | (b>>3); + //dbg_printf("convert %i r=%i g=%i b=%i to %i\n",c,r,g,b,R); + return R; +} +void setcolor(int c){ + gfx_SetColor(convertcolor(c)); + //gfx_SetTextTransparentColor(0); +} +void os_set_pixel(int x,int y,int c){ + setcolor(c); + gfx_SetPixel(x,y); +} +void os_fill_rect(int x,int y,int w,int h,int c){ + setcolor(c); + gfx_FillRectangle(x,y,w,h); +} +int os_get_pixel(int x,int y){ + return gfx_GetPixel(x,y); +} + +// FIXME? use gfx_SetTransparentColor with a value != FG and BG instead of fill rectangle +int os_draw_string_small(int x,int y,int c,int bg,const char * s,int fake){ + y+=STATUS_AREA_PX; + gfx_SetTextScale(1,1); + int dx=gfx_GetStringWidth(s); + if (!fake){ + gfx_SetColor(bg); + gfx_FillRectangle(x,y,dx,8); + int c_=gfx_SetTextFGColor(c); + int bg_=gfx_SetTextBGColor(bg); + gfx_PrintStringXY(s,x,y); + gfx_SetTextFGColor(c_); + gfx_SetTextBGColor(bg_); + } + return x+dx; +} +int os_draw_string_medium(int x,int y,int c,int bg,const char * s,int fake){ + y+=STATUS_AREA_PX; + gfx_SetTextScale(1,2); + //gfx_SetFontHeight(12); + int dx=gfx_GetStringWidth(s); + if (!fake){ + gfx_SetColor(bg); + gfx_FillRectangle(x,y,dx,16); + int c_=gfx_SetTextFGColor(c); + int bg_=gfx_SetTextBGColor(bg); + gfx_PrintStringXY(s,x,y); + gfx_SetTextFGColor(c_); + gfx_SetTextBGColor(bg_); + } + return x+dx; +} +int os_draw_string(int x,int y,int c,int bg,const char * s,int fake){ + y+=STATUS_AREA_PX; + gfx_SetTextScale(2,2); + int dx=gfx_GetStringWidth(s); + if (!fake){ + gfx_SetColor(bg); + gfx_FillRectangle(x,y,dx,16); + int c_=gfx_SetTextFGColor(c); + int bg_=gfx_SetTextBGColor(bg); + gfx_PrintStringXY(s,x,y); + gfx_SetTextFGColor(c_); + gfx_SetTextBGColor(bg_); + } + return x+dx; +} + +const int statuscolor=12345; +void statuslinemsg(const char * msg){ + os_draw_string(0,-STATUS_AREA_PX,statuscolor,SDK_BLACK,msg,false); +} + +void set_time(int h,int m){ + rtc_Set(rtc_Seconds,m,h,rtc_Days); +} + +void get_time(int *h,int *m){ + *h=rtc_Hours; + *m=rtc_Minutes; +} + +void display_time(){ + int h=rtc_Hours,m=rtc_Minutes; + char msg[10]; + msg[0]=' '; + msg[1]='0'+(h/10); + msg[2]='0'+(h%10); + msg[3]= 'h'; + msg[4]= ('0'+(m/10)); + msg[5]= ('0'+(m%10)); + msg[6]=0; + //msg[6]= 'm'; + //msg[7] = ('0'+(s/10)); + //msg[8] = ('0'+(s%10)); + //msg[9]=0; + os_fill_rect(270,0,LCD_WIDTH_PX-270,15,SDK_BLACK); + os_draw_string_medium(270,-STATUS_AREA_PX,statuscolor,SDK_BLACK,msg,false); +} + +void statusflags(){ + char *msg=0; + if (alpha==2){ + msg=alphalock?"alock":"alpha"; + } + else if (alpha==1){ + msg=alphalock?"ALOCK":"ALPHA"; + } + else { + if (shift) + msg="2nd"; + else + msg=""; + } + os_fill_rect(0,0,LCD_WIDTH_PX,16,SDK_BLACK); + os_draw_string_medium(225,-STATUS_AREA_PX,statuscolor,SDK_BLACK,msg,false); + os_draw_string_medium(160,-STATUS_AREA_PX,statuscolor,SDK_BLACK,os_get_angle_unit()?" rad ":" deg ",false); + display_time(); +} +void statusline(int mode){ + statusflags(); + if (mode==0) + os_draw_string_medium(190,-STATUS_AREA_PX,statuscolor,SDK_BLACK," CAS ",false); + if (mode==0) + return; + sync_screen(); +} +#endif + +#ifdef NSPIRE_NEWLIB +// NB changes for the nspire cx ii +// on_key_pressed() should be modified (returns always true) +// https://hackspire.org/index.php?title=Memory-mapped_I/O_ports_on_CX_II#90140000_-_Power_management +// cx ii power management 0x90140000, +// cx 900B0018 (R/W), 900B0020 (?) +// 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. +// 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). +// -> cx ii The OS controls the LCD backlight by writing to 90130018. +#include +#include "os.h" // Ndless/ndless-sdk/include/os.h +#include +#include +#include +#include +#include "k_defs.h" + +void sdk_init(void){ + lcd_init(lcd_type()); // clrscr(); +} + +void sdk_end(void){ + lcd_init(SCR_TYPE_INVALID); + refresh_osscr(); +} + +int c_rgb565to888(int c){ + c &= 0xffff; + int r=(c>>11)&0x1f,g=(c>>5)&0x3f,b=c&0x1f; + return (r<<19)|(g<<10)|(b<<3); +} + +const int nspire_statusarea=18; +int nspireemu=false; + +int waitforvblank(){ + return 0; +} + +int back_key_pressed(){ + return isKeyPressed(KEY_NSPIRE_DEL); +} +// next 3 functions may be void if not inside a window class hierarchy +void os_show_graph(){} // show graph inside Python shell (Numworks), not used +void os_hide_graph(){} // hide graph, not used anymore +void os_redraw(){} // force redraw of window class hierarchy + +int os_set_angle_unit(int mode){ + return false; +} +int os_get_angle_unit(){ + return 0; +} + +double millis(){ + unsigned NSPIRE_RTC_ADDR=0x90090000; + unsigned t1= * (volatile unsigned *) NSPIRE_RTC_ADDR; + return 1000.0*t1; +} + + +void get_hms(int *h,int *m,int *s){ + unsigned NSPIRE_RTC_ADDR=0x90090000; + unsigned t1= * (volatile unsigned *) NSPIRE_RTC_ADDR; + if (exam_mode){ + unsigned t=t1-exam_start; + if (exam_duration>0 && t>exam_duration){ + ;//set_exam_mode(0); + } + else { + if (exam_duration>0) + t1=exam_duration-t; + else { + if (exam_duration<0 && t<-exam_duration) + t1=-exam_duration-t; + } + } + } + unsigned d=t1/86400; + *s=t1%86400; + *h=*s/3600; + *m=(*s-3600* *h)/60; + *s%=60; +} + +void get_time(int *h,int *m){ + int s; + get_hms(h,m,&s); +} + +void set_time(int h,int m){ + // FIXME +} + +#ifndef is_cx2 +#define is_cx2 false +#endif + +double loopsleep(int ms){ + double n=ms*(is_cx2?3000:1000),j=0.0; + for (double i=0;iNSPIRE_FILEBUFFER-1){ + fclose(f); + return 0; + } + for (int i=0;iFILENAME_MAXRECORDS-1) + maxrecords=FILENAME_MAXRECORDS-1; + dp = opendir ("."); + if (dp == NULL){ + filenames[0]=0; + return 0; + } + int cur=0; + while ( (ep = readdir (dp)) && curd_name,*ext=0; + int l=strlen(s_),j; + char s[l+1]; + strcpy(s,s_); + for (j=l-1;j>0;--j){ + if (s[j]=='.'){ + ext=s+j+1; + break; + } + } + if (ext && strcmp(ext,"tns")==0){ + s[j]=0; + for (;j>0;--j){ + if (s[j]=='.'){ + ext=s+j+1; + break; + } + } + } + if (ext && strcmp(ext,extension)==0){ + if (exam_mode && + (strcmp(s_,"session.xw")!=0 && + strcmp(s_,"session.xw.tns")!=0 && + strcmp(s_,"session.py")!=0 && + strcmp(s_,"session.py.tns")!=0 + ) + ) + continue; + strncpy(os_filenames[cur],s_,FILENAME_MAXSIZE); + filenames[cur]=os_filenames[cur]; + ++cur; + } + } + closedir (dp); + filenames[cur]=NULL; +#if 0 + qsort(filenames,cur,sizeof(char *),c_trialpha); +#else + // qsort would be faster for large n, but here n0){ + finished=false; + const char * tmp=filenames[i-1]; + filenames[i-1]=filenames[i]; + filenames[i]=tmp; + } + } + if (finished) + break; + } +#endif + return cur; +} + +Gc nspire_gc=0; + +void reset_gc(){ + if (nspire_gc){ + gui_gc_finish(nspire_gc); + //gui_gc_free(nspire_gc); + } + nspire_gc=0; +} + +Gc * get_gc(){ + if (!nspire_gc){ + nspire_gc=gui_gc_global_GC(); + gui_gc_setRegion(nspire_gc, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + gui_gc_begin(nspire_gc); + } + return &nspire_gc; +} + +void os_set_pixel(int x,int y,int c){ + get_gc(); + gui_gc_setColor(nspire_gc,c_rgb565to888(c)); + gui_gc_drawRect(nspire_gc,x,y+nspire_statusarea,0,0); +} + +void os_fill_rect(int x,int y,int w,int h,int c){ + get_gc(); + gui_gc_setColor(nspire_gc,c_rgb565to888(c)); + gui_gc_fillRect(nspire_gc,x,y+nspire_statusarea,w,h); +} + +int os_get_pixel(int x,int y){ + if (x<0 || x>=SCREEN_WIDTH || y<0 || y>=SCREEN_HEIGHT) + return -1; +#if 1 + get_gc(); + char ** off_buff = ((((char *****)nspire_gc)[9])[0])[0x8]; + int res = *(unsigned short *) (off_buff[y+nspire_statusarea] + 2*x); + return res; +#else + unsigned short * addr=*(unsigned short **) 0xC0000010; + int r=addr[(y+nspire_statusarea)*SCREEN_WIDTH+x]; + return r; +#endif +} + +int nspire_draw_string(int x,int y,int c,int bg,int f,const char * s,int fake){ + // 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. + int l=strlen(s); + char utf16[2*l+2]; + ascii2utf16(utf16,s,l); + utf16[2*l]=0; + utf16[2*l+1]=0; + get_gc(); + gui_gc_setFont(nspire_gc,f); + int dx=gui_gc_getStringWidth(nspire_gc, f, utf16, 0, l) ; + if (fake) + return x+dx; + int dy=17; + if (f==Regular9) + dy=13; + if (f==Regular11) + dy=16; + gui_gc_setColor(nspire_gc,c_rgb565to888(bg)); + gui_gc_fillRect(nspire_gc,x,y,dx,dy); + gui_gc_setColor(nspire_gc,c_rgb565to888(c)); + //gui_gc_setPen(nspire_gc, GC_PS_MEDIUM, GC_PM_SMOOTH); + gui_gc_drawString(nspire_gc, utf16, x, y-1, GC_SM_NORMAL | GC_SM_TOP); // normal mode + return x+dx; +} + +int os_draw_string(int x,int y,int c,int bg,const char * s,int fake){ + get_gc(); + gui_gc_clipRect(nspire_gc,0,nspire_statusarea,SCREEN_WIDTH,SCREEN_HEIGHT-nspire_statusarea,0); + int i=nspire_draw_string(x,y+nspire_statusarea,c,bg,Regular12,s,fake); + gui_gc_clipRect(nspire_gc,0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GC_CRO_RESET); + return i; +} +int os_draw_string_small(int x,int y,int c,int bg,const char * s,int fake){ + get_gc(); + gui_gc_clipRect(nspire_gc,0,nspire_statusarea,SCREEN_WIDTH,SCREEN_HEIGHT-nspire_statusarea,GC_CRO_SET); + int i=nspire_draw_string(x,y+nspire_statusarea,c,bg,Regular9,s,fake); + gui_gc_clipRect(nspire_gc,0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GC_CRO_RESET); + return i; +} + +int os_draw_string_medium(int x,int y,int c,int bg,const char * s,int fake){ + get_gc(); + gui_gc_clipRect(nspire_gc,0,nspire_statusarea,SCREEN_WIDTH,SCREEN_HEIGHT-nspire_statusarea,GC_CRO_SET); + int i=nspire_draw_string(x,y+nspire_statusarea,c,bg,Regular11,s,fake); + gui_gc_clipRect(nspire_gc,0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GC_CRO_RESET); + return i; +} + +void statuslinemsg(const char * msg){ + get_gc(); + int bg=exam_bg(); + gui_gc_setColor(nspire_gc,c_rgb565to888(bg)); + gui_gc_fillRect(nspire_gc,0,0,SCREEN_WIDTH,nspire_statusarea); + nspire_draw_string(0,0,exam_mode?0xffff:0,bg,Regular9,msg,false); + if (nspireemu) + nspire_draw_string(190,0,exam_mode?0xffff:0,bg,Regular9," emu ",false); + else + nspire_draw_string(190,0,exam_mode?0xffff:0,bg,Regular9," CAS ",false); +} + +void display_time(){ + int h,m,s; + get_hms(&h,&m,&s); + char msg[10]; + msg[0]=' '; + msg[1]='0'+(h/10); + msg[2]='0'+(h%10); + msg[3]= 'h'; + msg[4]= ('0'+(m/10)); + msg[5]= ('0'+(m%10)); + msg[6]=0; + //msg[6]= 'm'; + //msg[7] = ('0'+(s/10)); + //msg[8] = ('0'+(s%10)); + //msg[9]=0; + int bg=exam_bg(); + gui_gc_setColor(nspire_gc,c_rgb565to888(bg)); + gui_gc_fillRect(nspire_gc,270,0,SCREEN_WIDTH-270,nspire_statusarea); + nspire_draw_string(270,0,exam_mode?0xffff:0,bg,Regular9,msg,false); +} + +void sync_screen(){ + get_gc(); + //gui_gc_finish(nspire_gc); + gui_gc_blit_to_screen(nspire_gc); + ck_msleep(10); + //nspire_gc=0; + // gui_gc_begin(nspire_gc); +} + +// Nspire peripheral reset : +// https://github.com/nDroidProject/nDroid-bootloader/blob/master/kernel.c +// https://hackspire.org/index.php?title=Memory-mapped_I/O_ports_on_CX#CC000000_-_SHA-256_hash_generator +// hardware ports +// https://hackspire.org/index.php?title=Memory-mapped_I/O_ports_on_CX + +int nspire_shift=0; +int nspire_ctrl=0; +int nspire_select=false; +void statusline(int mode){ + char *msg=0; + if (nspire_ctrl){ + if (nspire_shift) + msg="shift ctrl"; + else + msg=" ctrl"; + } + else { + if (nspire_shift) + msg="shift"; + else + msg=""; + } + int bg=exam_bg(); + gui_gc_setColor(nspire_gc,c_rgb565to888(bg)); + gui_gc_fillRect(nspire_gc,210,0,SCREEN_WIDTH-210,nspire_statusarea); + nspire_draw_string(224,0,exam_mode?0xffff:0,bg,Regular9,msg,false); + if (nspireemu) + nspire_draw_string(190,0,exam_mode?0xffff:0,bg,Regular9," emu ",false); + else + nspire_draw_string(190,0,0xf800,bg,Regular9," CAS ",false); + display_time(); + if (mode==0) + return; + sync_screen(); +} + + +#define SHIFTCTRL(x, y, z) (nspire_ctrl ? (z) : nspire_shift ? (y) : (x)) +#define SHIFT(x, y) SHIFTCTRL(x, y, x) +#define CTRL(x, y) SHIFTCTRL(x, x, y) +#define NORMAL(x) SHIFTCTRL(x, x, x) + +int nspire_scan(int * adaptive_cursor_state){ + if (isKeyPressed(KEY_NSPIRE_CTRL)){ + while (isKeyPressed(KEY_NSPIRE_CTRL)) + ; + nspire_ctrl=!nspire_ctrl; + nspire_shift=0; + statusline(1); + return -2; + } + if (isKeyPressed(KEY_NSPIRE_SHIFT)){ + while (isKeyPressed(KEY_NSPIRE_SHIFT)) + ; + nspire_shift=!nspire_shift; + nspire_ctrl=0; + statusline(1); + return -1; + } + *adaptive_cursor_state = SHIFTCTRL(0, 1, 4); + if (isKeyPressed(KEY_NSPIRE_LEFT)|| isKeyPressed(KEY_NSPIRE_LEFTUP) || isKeyPressed(KEY_NSPIRE_DOWNLEFT)) + return SHIFTCTRL(KEY_CTRL_LEFT,KEY_SHIFT_LEFT,KEY_LEFT_CTRL); + if (isKeyPressed(KEY_NSPIRE_RIGHT)|| isKeyPressed(KEY_NSPIRE_UPRIGHT) || isKeyPressed(KEY_NSPIRE_RIGHTDOWN)) + return SHIFTCTRL(KEY_CTRL_RIGHT,KEY_SHIFT_RIGHT,KEY_RIGHT_CTRL); + if (isKeyPressed(KEY_NSPIRE_UP)) + return SHIFTCTRL(KEY_CTRL_UP,KEY_CTRL_PAGEUP,KEY_UP_CTRL); + if (isKeyPressed(KEY_NSPIRE_DOWN)) + return SHIFTCTRL(KEY_CTRL_DOWN,KEY_CTRL_PAGEDOWN,KEY_DOWN_CTRL); + + if (isKeyPressed(KEY_NSPIRE_ESC)) return KEY_CTRL_EXIT ; + if (isKeyPressed(KEY_NSPIRE_HOME)) return SHIFTCTRL(KEY_CTRL_MENU,KEY_CTRL_MENU,KEY_CTRL_AC) ; + if (isKeyPressed(KEY_NSPIRE_MENU)) return KEY_CTRL_CATALOG ; + + // Characters + if (isKeyPressed(KEY_NSPIRE_A)) return SHIFTCTRL('a','A',KEY_CTRL_A); + if (isKeyPressed(KEY_NSPIRE_B)) return SHIFTCTRL('b','B',KEY_BOOK); + if (isKeyPressed(KEY_NSPIRE_C)) return SHIFTCTRL('c','C',KEY_CTRL_CLIP); + if (isKeyPressed(KEY_NSPIRE_D)) return SHIFTCTRL('d','D',KEY_CTRL_D); + if (isKeyPressed(KEY_NSPIRE_E)) return SHIFTCTRL('e','E',KEY_CTRL_F10); + if (isKeyPressed(KEY_NSPIRE_F)) return SHIFTCTRL('f','F',KEY_CTRL_F11); + if (isKeyPressed(KEY_NSPIRE_G)) return SHIFTCTRL('g','G',KEY_CTRL_F12); + if (isKeyPressed(KEY_NSPIRE_H)) return SHIFTCTRL('h','H',KEY_CTRL_F13); + if (isKeyPressed(KEY_NSPIRE_I)) return SHIFTCTRL('i','I',KEY_CTRL_F14); + if (isKeyPressed(KEY_NSPIRE_J)) return SHIFTCTRL('j','J',KEY_CTRL_F15); + if (isKeyPressed(KEY_NSPIRE_K)) return SHIFTCTRL('k','K',KEY_CTRL_AC); + if (isKeyPressed(KEY_NSPIRE_L)) return SHIFTCTRL('l','L',KEY_CTRL_F14); + if (isKeyPressed(KEY_NSPIRE_M)) return SHIFTCTRL('m','M',KEY_CTRL_CATALOG); + if (isKeyPressed(KEY_NSPIRE_N)) return SHIFTCTRL('n','N',KEY_CTRL_N); + if (isKeyPressed(KEY_NSPIRE_O)) return SHIFTCTRL('o','O',KEY_SHIFT_OPTN); + if (isKeyPressed(KEY_NSPIRE_P)) return SHIFTCTRL('p','P',KEY_CTRL_PRGM); + if (isKeyPressed(KEY_NSPIRE_Q)) return SHIFT('q','Q'); + if (isKeyPressed(KEY_NSPIRE_R)) return SHIFTCTRL('r','R',KEY_CTRL_R); + if (isKeyPressed(KEY_NSPIRE_S)) return SHIFTCTRL('s','S',KEY_CTRL_S); + if (isKeyPressed(KEY_NSPIRE_T)) return SHIFTCTRL('t','T',KEY_CTRL_T); + if (isKeyPressed(KEY_NSPIRE_U)) return SHIFTCTRL('u','U',KEY_CTRL_F13); + if (isKeyPressed(KEY_NSPIRE_V)) return SHIFTCTRL('v','V',KEY_CTRL_PASTE); + if (isKeyPressed(KEY_NSPIRE_W)) return SHIFT('w','W'); + if (isKeyPressed(KEY_NSPIRE_X)) return SHIFTCTRL('x','X',KEY_CTRL_CUT); + if (isKeyPressed(KEY_NSPIRE_Y)) return SHIFT('y','Y'); + if (isKeyPressed(KEY_NSPIRE_Z)) return SHIFTCTRL('z','Z',KEY_CTRL_UNDO); + + // Numbers + if (nspireemu){ // for firebird, redefine ctrl + if (isKeyPressed(KEY_NSPIRE_0)) return SHIFTCTRL('0',KEY_CTRL_F10,')'); + if (isKeyPressed(KEY_NSPIRE_1)) return SHIFTCTRL('1',KEY_CTRL_F1,'!'); + if (isKeyPressed(KEY_NSPIRE_2)) return SHIFTCTRL('2',KEY_CTRL_F2,'@'); + if (isKeyPressed(KEY_NSPIRE_3)) return SHIFTCTRL('3',KEY_CTRL_F3,'#'); + if (isKeyPressed(KEY_NSPIRE_4)) return SHIFTCTRL('4',KEY_CTRL_F4,'$'); + if (isKeyPressed(KEY_NSPIRE_5)) return SHIFTCTRL('5',KEY_CTRL_F5,'%'); + if (isKeyPressed(KEY_NSPIRE_6)) return SHIFTCTRL('6',KEY_CTRL_F6,'^'); + if (isKeyPressed(KEY_NSPIRE_7)) return SHIFTCTRL('7',KEY_CTRL_F7,'&'); + if (isKeyPressed(KEY_NSPIRE_8)) return SHIFTCTRL('8',KEY_CTRL_F8,'*'); + if (isKeyPressed(KEY_NSPIRE_9)) return SHIFTCTRL('9',KEY_CTRL_F9,'('); + } + else { + if (isKeyPressed(KEY_NSPIRE_0)) return SHIFTCTRL('0',KEY_CTRL_F10,KEY_CTRL_F10); + if (isKeyPressed(KEY_NSPIRE_1)) return SHIFTCTRL('1',KEY_CTRL_F1,KEY_CTRL_F1); + if (isKeyPressed(KEY_NSPIRE_2)) return SHIFTCTRL('2',KEY_CTRL_F2,KEY_CTRL_F2); + if (isKeyPressed(KEY_NSPIRE_3)) return SHIFTCTRL('3',KEY_CTRL_F3,KEY_CTRL_F3); + if (isKeyPressed(KEY_NSPIRE_4)) return SHIFTCTRL('4',KEY_CTRL_F4,KEY_CTRL_F4); + if (isKeyPressed(KEY_NSPIRE_5)) return SHIFTCTRL('5',KEY_CTRL_F5,KEY_CTRL_F5); + if (isKeyPressed(KEY_NSPIRE_6)) return SHIFTCTRL('6',KEY_CTRL_F6,KEY_CTRL_F6); + if (isKeyPressed(KEY_NSPIRE_7)) return SHIFTCTRL('7',KEY_CTRL_F7,KEY_CTRL_F7); + if (isKeyPressed(KEY_NSPIRE_8)) return SHIFTCTRL('8',KEY_CTRL_F8,KEY_CTRL_F8); + if (isKeyPressed(KEY_NSPIRE_9)) return SHIFTCTRL('9',KEY_CTRL_F9,KEY_CTRL_F9); + } + + // Symbols + if (isKeyPressed(KEY_NSPIRE_FRAC)) return SHIFTCTRL(KEY_EQW_TEMPLATE,KEY_AFFECT,KEY_AFFECT); + if (isKeyPressed(KEY_NSPIRE_SQU)) return CTRL(KEY_CHAR_SQUARE,KEY_CHAR_ROOT); + if (isKeyPressed(KEY_NSPIRE_TENX)) return CTRL(KEY_CHAR_EXPN10,KEY_CHAR_LOG); + if (isKeyPressed(KEY_NSPIRE_eEXP)) return CTRL(KEY_CHAR_EXPN,KEY_CHAR_LN); + if (isKeyPressed(KEY_NSPIRE_COMMA)) return SHIFTCTRL(',',';',':'); + if (isKeyPressed(KEY_NSPIRE_PERIOD)) return SHIFTCTRL('.',KEY_CTRL_F11,':'); + if (isKeyPressed(KEY_NSPIRE_COLON)) return NORMAL(':'); + if (isKeyPressed(KEY_NSPIRE_LP)) return SHIFTCTRL('(',KEY_CTRL_F13,KEY_CHAR_CROCHETS); + if (isKeyPressed(KEY_NSPIRE_RP)) return SHIFTCTRL(')',KEY_CTRL_F14,KEY_CHAR_ACCOLADES); + if (isKeyPressed(KEY_NSPIRE_SPACE)) return SHIFTCTRL(' ','_','_'); + if (isKeyPressed(KEY_NSPIRE_DIVIDE)) + return SHIFTCTRL('/','%','\\'); + if (isKeyPressed(KEY_NSPIRE_MULTIPLY)) return SHIFTCTRL('*','\'','\"'); + if (isKeyPressed(KEY_NSPIRE_MINUS)) return SHIFTCTRL('-','_', '<'); + if (isKeyPressed(KEY_NSPIRE_NEGATIVE)) return SHIFTCTRL('-',KEY_CTRL_F12,KEY_CHAR_ANS); + if (isKeyPressed(KEY_NSPIRE_PLUS)) return SHIFTCTRL('+', KEY_CHAR_NORMAL,'>'); + if (isKeyPressed(KEY_NSPIRE_EQU)) return SHIFTCTRL('=', '|',KEY_CHAR_STORE); + if (isKeyPressed(KEY_NSPIRE_LTHAN)) return NORMAL('<'); + if (isKeyPressed(KEY_NSPIRE_GTHAN)) return NORMAL('>'); + if (isKeyPressed(KEY_NSPIRE_QUOTE)) return NORMAL('\"'); + if (isKeyPressed(KEY_NSPIRE_APOSTROPHE)) return NORMAL('\''); + if (isKeyPressed(KEY_NSPIRE_QUES)) return SHIFTCTRL('?','|','!'); + if (isKeyPressed(KEY_NSPIRE_QUESEXCL)) return SHIFTCTRL('?','|','!'); + if (isKeyPressed(KEY_NSPIRE_BAR)) return NORMAL('|'); + if (isKeyPressed(KEY_NSPIRE_EXP)) return SHIFT('^',KEY_CHAR_RECIP); + if (isKeyPressed(KEY_NSPIRE_EE)) return SHIFTCTRL('%','&', '@'); + if (isKeyPressed(KEY_NSPIRE_PI)) return KEY_CHAR_PI; + if (isKeyPressed(KEY_NSPIRE_FLAG)) return SHIFTCTRL(';',':',KEY_CHAR_IMGNRY); + if (isKeyPressed(KEY_NSPIRE_ENTER)) return SHIFTCTRL(KEY_CTRL_OK,'~',KEY_CTRL_EXE); + if (isKeyPressed(KEY_NSPIRE_TRIG)) return SHIFTCTRL(KEY_CHAR_SIN,KEY_CHAR_COS,KEY_CHAR_TAN); + + // Special chars + if (isKeyPressed(KEY_NSPIRE_SCRATCHPAD)) return SHIFTCTRL(KEY_CTRL_SETUP,KEY_LOAD,KEY_SAVE); + if (isKeyPressed(KEY_NSPIRE_VAR)) return SHIFTCTRL(KEY_CTRL_VARS,KEY_CHAR_FACTOR,KEY_CHAR_STORE); + if (isKeyPressed(KEY_NSPIRE_DOC)) return SHIFT(KEY_CTRL_MENU,KEY_CTRL_SD); + if (isKeyPressed(KEY_NSPIRE_CAT)) return KEY_BOOK; + if (isKeyPressed(KEY_NSPIRE_DEL)) return SHIFTCTRL(KEY_CTRL_DEL,KEY_CTRL_DEL,KEY_CTRL_AC); + if (isKeyPressed(KEY_NSPIRE_RET)) return KEY_CTRL_EXE; + if (isKeyPressed(KEY_NSPIRE_TAB)) return '\t'; + + return 0; +} + +int ascii_get(int* adaptive_cursor_state){ + int res=nspire_scan(adaptive_cursor_state);; + return res; +} + +int handle_f5(){return 0;} +int iskeydown(int key){ + t_key t=KEY_NSPIRE_SPACE; + switch (key){ + case 0: + t=KEY_NSPIRE_LEFT; + break; + case 1: + t=KEY_NSPIRE_UP; + break; + case 2: + t=KEY_NSPIRE_DOWN; + break; + case 3: + t=KEY_NSPIRE_RIGHT; + break; + case 4: + t=KEY_NSPIRE_ENTER; + break; + case 5: + t=KEY_NSPIRE_ESC; + break; + case 6: + t=KEY_NSPIRE_HOME; + break; + case 7: + t=KEY_NSPIRE_MENU; + break; + case 12: + t=KEY_NSPIRE_SHIFT; + break; + case 13: + t=KEY_NSPIRE_CTRL; + break; + case 14: + t=KEY_NSPIRE_SCRATCHPAD; + break; + case 15: + t=KEY_NSPIRE_VAR; + break; + case 16: + t=KEY_NSPIRE_DOC; + break; + case 17: + t=KEY_NSPIRE_DEL; + break; + case 18: + t=KEY_NSPIRE_eEXP; + break; + case 19: + t=KEY_NSPIRE_EQU; + break; + case 20: + t=KEY_NSPIRE_TENX; + break; + case 21: + t=KEY_NSPIRE_I; + break; + case 22: + t=KEY_NSPIRE_COMMA; + break; + case 23: + t=KEY_NSPIRE_EXP; + break; + case 24: + t=KEY_NSPIRE_TRIG; + break; + case 25: + t=KEY_NSPIRE_C; + break; + case 26: + t=KEY_NSPIRE_T; + break; + case 27: + t=KEY_NSPIRE_PI; + break; + case 28: + t=KEY_NSPIRE_S; + break; + case 29: + t=KEY_NSPIRE_SQU; + break; + case 30: + t=KEY_NSPIRE_7; + break; + case 31: + t=KEY_NSPIRE_8; + break; + case 32: + t=KEY_NSPIRE_9; + break; + case 33: + t=KEY_NSPIRE_LP; + break; + case 34: + t=KEY_NSPIRE_RP; + break; + case 36: + t=KEY_NSPIRE_4; + break; + case 37: + t=KEY_NSPIRE_5; + break; + case 38: + t=KEY_NSPIRE_6; + break; + case 39: + t=KEY_NSPIRE_MULTIPLY; + break; + case 40: + t=KEY_NSPIRE_DIVIDE; + break; + case 42: + t=KEY_NSPIRE_1; + break; + case 43: + t=KEY_NSPIRE_2; + break; + case 44: + t=KEY_NSPIRE_3; + break; + case 45: + t=KEY_NSPIRE_PLUS; + break; + case 46: + t=KEY_NSPIRE_MINUS; + break; + case 48: + t=KEY_NSPIRE_0; + break; + case 49: + t=KEY_NSPIRE_PERIOD; + break; + case 50: + t=KEY_NSPIRE_EE; + break; + case 51: + t=KEY_NSPIRE_NEGATIVE; + break; + case 52: + t=KEY_NSPIRE_RET; + break; + } + return isKeyPressed(t); +} + + +// ? see also ndless-sdk/thirdparty/nspire-io/arch-nspire/nspire.c nio_ascii_get +int getkey(int allow_suspend){ + sync_screen(); + if (shutdown_state) + return KEY_SHUTDOWN; + int lastkey=-1; + unsigned NSPIRE_RTC_ADDR=0x90090000; + static unsigned lastt=0; + for (;;){ + unsigned t1= * (volatile unsigned *) NSPIRE_RTC_ADDR; + if (lastt==0) + lastt=t1; + if (t1-lastt>10){ + display_time(); + sync_screen(); + } + int autosuspend=(t1-lastt>=100); + if (nspire_exam_mode!=2 && + is_cx2 && nspire_ctrl && on_key_pressed()){ + os_fill_rect(50,90,200,40,0x1234); + nspire_draw_string(60,120,0,0xffff,Regular12,"Quit KhiCAS to shutdown",false); + nspire_ctrl=false; + statusline(1); + continue; + } + if ( (nspire_exam_mode==2 || !is_cx2) && + allow_suspend && (autosuspend || (nspire_ctrl && on_key_pressed()))){ + nspire_ctrl=nspire_shift=false; + while (!autosuspend && on_key_pressed()) + loopsleep(10); + // somewhat OFF by setting LCD to 0 + unsigned NSPIRE_CONTRAST_ADDR=is_cx2?0x90130014:0x900f0020; + unsigned oldval=*(volatile unsigned *)NSPIRE_CONTRAST_ADDR,oldval2; + if (is_cx2){ + oldval2=*(volatile unsigned *) (NSPIRE_CONTRAST_ADDR+4); + *(volatile unsigned *) (NSPIRE_CONTRAST_ADDR+4)=0xffff; + } + *(volatile unsigned *)NSPIRE_CONTRAST_ADDR=is_cx2?0xffff:0x100; + static volatile uint32_t *lcd_controller = (volatile uint32_t*) 0xC0000000; + lcd_controller[6] &= ~(0b1 << 11); + loopsleep(20); + lcd_controller[6] &= ~ 0b1; + unsigned offtime=* (volatile unsigned *) NSPIRE_RTC_ADDR; + for (int n=0;!on_key_pressed();++n){ + loopsleep(100); + idle(); + if (!exam_mode && nspire_exam_mode!=2 && shutdown + // && n&0xff==0 + ){ + unsigned curtime=* (volatile unsigned *) NSPIRE_RTC_ADDR; + if (curtime-offtime>7200){ + shutdown_state=1; + // after 2 hours, leave KhiCAS + // that way the OS will really shutdown the calc + lcd_controller[6] |= 0b1; + loopsleep(20); + lcd_controller[6]|= 0b1 << 11; + if (is_cx2) + *(volatile unsigned *)(NSPIRE_CONTRAST_ADDR+4)=oldval2; + *(volatile unsigned *)NSPIRE_CONTRAST_ADDR=oldval; + statuslinemsg("Press ON to disable KhiCAS auto shutdown"); + //os_fill_rect(0,0,320,222,0xffff); + sync_screen(); + int m=0,mmax=150; + for (;m=KEY_CTRL_LEFT && i<=KEY_CTRL_RIGHT) || + (i>=KEY_UP_CTRL && i<=KEY_RIGHT_CTRL) || + (i>=KEY_SELECT_LEFT && i<=KEY_SELECT_RIGHT) || + i==KEY_CTRL_DEL){ + int delay=(lastkey==i)?5:60,j; + for (j=0;j=0 && h>=0) + os_fill_rect(x,y,w,h,c); + } + int os_get_pixel(int x,int y); + /* returns new x position */ +#ifdef __cplusplus + int os_draw_string(int x,int y,int c,int bg,const char * s,int fake=0); +#else + int os_draw_string(int x,int y,int c,int bg,const char * s,int fake); +#endif + inline int os_draw_string_(int x,int y,const char * s){ return os_draw_string(x,y,SDK_BLACK,SDK_WHITE,s,0);} +#ifdef __cplusplus + int os_draw_string_small(int x,int y,int c,int bg,const char * s,int fake=0); +#else + int os_draw_string_small(int x,int y,int c,int bg,const char * s,int fake); +#endif + inline int os_draw_string_small_(int x,int y,const char * s){ return os_draw_string_small(x,y,SDK_BLACK,SDK_WHITE,s,0);} + +#ifdef __cplusplus +#ifdef NUMWORKS + inline int os_draw_string_medium(int x,int y,int c,int bg,const char * s,int fake=0){ return os_draw_string_small(x,y,c,bg,s,fake);} +#else + int os_draw_string_medium(int x,int y,int c,int bg,const char * s,int fake=0); +#endif +#else +#ifdef NUMWORKS + inline int os_draw_string_medium(int x,int y,int c,int bg,const char * s,int fake){ return os_draw_string_small(x,y,c,bg,s,fake);} +#else + int os_draw_string_medium(int x,int y,int c,int bg,const char * s,int fake); +#endif +#endif + inline int os_draw_string_medium_(int x,int y,const char * s){ return os_draw_string_medium(x,y,SDK_BLACK,SDK_WHITE,s,0);} + + inline void Printmini(int x,int y,const char * s,int i){ + // dbg_printf("Printmini %i %i %s %i\n",x,y,s,i); +#if defined FX || defined FXCG + os_draw_string_small(x,y,i?0xffff:0,i?0:0xffff,s,false); +#else + os_draw_string_small(x,y,SDK_BLACK,i?COLOR_SELECTED:SDK_WHITE,s,false); +#endif + } + + inline void Printxy(int x,int y,const char * s,int i){ os_draw_string_medium(x,y,0,i?COLOR_SELECTED:0xffff,s,false);} + inline void PrintXY(int x,int y,const char * s,int i){ Printxy(3*x,3*y,s,i);} + + void GetKey(int * key); + int getkey(int allow_suspend); // transformed + inline void ck_getkey(int *key){ GetKey(key);} + void enable_back_interrupt(); + inline void set_abort(){ enable_back_interrupt(); } + void disable_back_interrupt(); + inline void clear_abort(){ disable_back_interrupt(); } + int isalphaactive(); + int alphawasactive(int * key); + void lock_alpha(); + void reset_kbd(); + int handle_f5(); + void statuslinemsg(const char * msg); +#ifdef __cplusplus + void statusline(int mode=0); +#else + void statusline(int mode); +#endif + void statusflags(void); +#ifdef NUMWORKS + inline int iskeydown(int key){ return getkey(key | 0x80000000); } +#else + int iskeydown(int key); +#endif + +#if defined NSPIRE || defined NSPIRE_NEWLIB + extern int nspire_shift,nspire_ctrl; + double loopsleep(int ms); +#include + Gc * get_gc(); + int c_rgb565to888(int c); + int nspire_scan(int * adaptive_cursor_state); + +#define max_heap_size 60 + extern int nspireemu; + extern char nspire_filebuf[NSPIRE_FILEBUFFER]; + extern int on_key_enabled; + void get_hms(int *h,int *m,int *s); + void reset_gc(); +#endif + + extern int (*shutdown)(); // function called after 2 hours of idle + extern short int shutdown_state; + inline void Bdisp_PutDisp_DD(void){ sync_screen(); } + inline void sprint_int(char * c,int i){ sprintf(c,"%d",i);} + inline void sprint_double(char * c,double d){ sprintf(c,"%.4g",d);} + int GetSetupSetting(int k); + inline int Setup_GetEntry(int k){ return GetSetupSetting(k); } + void SetQuitHandler( void (*f)(void)); +#define RTC_GetTicks millis +#ifndef TICE + inline void clear_screen(void){os_fill_rect(0,0,LCD_WIDTH_PX,LCD_HEIGHT_PX,SDK_WHITE);} +#endif + inline void Bdisp_AllClr_VRAM(void){ clear_screen(); } + +#ifdef __cplusplus +} +#endif +#endif // K_CSDK_H diff --git a/ion/src/simulator/nspire/k_defs.h b/ion/src/simulator/nspire/k_defs.h new file mode 100755 index 00000000000..ce395b192c3 --- /dev/null +++ b/ion/src/simulator/nspire/k_defs.h @@ -0,0 +1,211 @@ +#ifndef K_DEFS_H +#define K_DEFS_H +#define NSPIRE_FILEBUFFER 512*1024 + // Character codes +#define KEY_CHAR_0 0x30 +#define KEY_CHAR_1 0x31 +#define KEY_CHAR_2 0x32 +#define KEY_CHAR_3 0x33 +#define KEY_CHAR_4 0x34 +#define KEY_CHAR_5 0x35 +#define KEY_CHAR_6 0x36 +#define KEY_CHAR_7 0x37 +#define KEY_CHAR_8 0x38 +#define KEY_CHAR_9 0x39 +#define KEY_CHAR_DP 0x2e +#define KEY_CHAR_EXP 0x0f +#define KEY_CHAR_PMINUS 30200 +#define KEY_CHAR_PLUS 43 +#define KEY_CHAR_MINUS 45 +#define KEY_CHAR_MULT 42 +#define KEY_CHAR_DIV 47 +#define KEY_CHAR_FRAC 0xbb +#define KEY_CHAR_LPAR 0x28 +#define KEY_CHAR_RPAR 0x29 +#define KEY_CHAR_COMMA 0x2c +#define KEY_CHAR_STORE 0x0e +#define KEY_CHAR_LOG 0x95 +#define KEY_CHAR_LN 0x85 +#define KEY_CHAR_SIN 0x81 +#define KEY_CHAR_COS 0x82 +#define KEY_CHAR_TAN 0x83 +#define KEY_CHAR_SQUARE 0x8b +#define KEY_CHAR_POW 0xa8 +#define KEY_CHAR_IMGNRY 0x7f50 +#define KEY_CHAR_LIST 0x7f51 +#define KEY_CHAR_MAT 0x7f40 +#define KEY_CHAR_EQUAL 0x3d +#define KEY_CHAR_PI 0xd0 +#define KEY_CHAR_ANS 0xc0 +#define KEY_SHIFT_ANS 0xc1 +#define KEY_CHAR_LBRCKT 0x5b +#define KEY_CHAR_RBRCKT 0x5d +#define KEY_CHAR_LBRACE 0x7b +#define KEY_CHAR_RBRACE 0x7d +#define KEY_CHAR_CR 0x0d +#define KEY_CHAR_CUBEROOT 0x96 +#define KEY_CHAR_RECIP 0x9b +#define KEY_CHAR_ANGLE 0x7f54 +#define KEY_CHAR_EXPN10 0xb5 +#define KEY_CHAR_EXPN 0xa5 +#define KEY_CHAR_ASIN 0x91 +#define KEY_CHAR_ACOS 0x92 +#define KEY_CHAR_ATAN 0x93 +#define KEY_CHAR_ROOT 0x86 +#define KEY_CHAR_POWROOT 0xb8 +#define KEY_CHAR_SPACE 0x20 +#define KEY_CHAR_DQUATE 0x22 +#define KEY_CHAR_VALR 0xcd +#define KEY_CHAR_THETA 0xce +#define KEY_CHAR_FACTOR 0xda +#define KEY_CHAR_NORMAL 0xdb +#define KEY_CHAR_SHIFTMINUS 0xdc +#define KEY_CHAR_CROCHETS 0xdd +#define KEY_CHAR_ACCOLADES 0xde +#define KEY_CHAR_A 0x41 +#define KEY_CHAR_B 0x42 +#define KEY_CHAR_C 0x43 +#define KEY_CHAR_D 0x44 +#define KEY_CHAR_E 0x45 +#define KEY_CHAR_F 0x46 +#define KEY_CHAR_G 0x47 +#define KEY_CHAR_H 0x48 +#define KEY_CHAR_I 0x49 +#define KEY_CHAR_J 0x4a +#define KEY_CHAR_K 0x4b +#define KEY_CHAR_L 0x4c +#define KEY_CHAR_M 0x4d +#define KEY_CHAR_N 0x4e +#define KEY_CHAR_O 0x4f +#define KEY_CHAR_P 0x50 +#define KEY_CHAR_Q 0x51 +#define KEY_CHAR_R 0x52 +#define KEY_CHAR_S 0x53 +#define KEY_CHAR_T 0x54 +#define KEY_CHAR_U 0x55 +#define KEY_CHAR_V 0x56 +#define KEY_CHAR_W 0x57 +#define KEY_CHAR_X 0x58 +#define KEY_CHAR_Y 0x59 +#define KEY_CHAR_Z 0x5a + + + // Control codes +#define KEY_CTRL_FORMAT 30203 +#define KEY_CTRL_NOP 30202 +#define KEY_CTRL_EXE 30201 +#define KEY_CTRL_DEL 30025 +#define KEY_CTRL_AC 30070 +#define KEY_CTRL_FD 30046 +#define KEY_CTRL_UNDO 30045 +#define KEY_CTRL_XTT 30001 +#define KEY_CTRL_EXIT 5 +#define KEY_CTRL_OK 4 +#define KEY_CTRL_SHIFT 30006 +#define KEY_CTRL_ALPHA 30007 +#define KEY_CTRL_OPTN 30008 +#define KEY_CTRL_VARS 30030 +#define KEY_CTRL_UP 1 +#define KEY_CTRL_DOWN 2 +#define KEY_CTRL_LEFT 0 +#define KEY_CTRL_RIGHT 3 +#define KEY_CTRL_F1 30009 +#define KEY_CTRL_F2 30010 +#define KEY_CTRL_F3 30011 +#define KEY_CTRL_F4 30012 +#define KEY_CTRL_F5 30013 +#define KEY_CTRL_F6 30014 +#define KEY_CTRL_F7 30915 +#define KEY_CTRL_F8 30916 +#define KEY_CTRL_F9 30917 +#define KEY_CTRL_F10 30918 +#define KEY_CTRL_F11 30919 +#define KEY_CTRL_F12 30920 +#define KEY_CTRL_F13 30921 +#define KEY_CTRL_F14 30922 +#define KEY_CTRL_F15 30923 +#define KEY_CTRL_F16 30924 +#define KEY_CTRL_F17 30925 +#define KEY_CTRL_F18 30926 +#define KEY_CTRL_F19 30927 +#define KEY_CTRL_F20 30928 +#define KEY_CTRL_CATALOG 30100 +#define KEY_CTRL_CAPTURE 30055 +#define KEY_CTRL_CLIP 30050 +#define KEY_CTRL_CUT 30250 +#define KEY_CTRL_PASTE 30036 +#define KEY_CTRL_INS 30033 +#define KEY_CTRL_MIXEDFRAC 30054 +#define KEY_CTRL_FRACCNVRT 30026 +#define KEY_CTRL_QUIT 30029 +#define KEY_CTRL_PRGM 30028 +#define KEY_CTRL_SETUP 30037 +#define KEY_CTRL_PAGEUP 30052 +#define KEY_CTRL_PAGEDOWN 30053 +#define KEY_CTRL_MENU 30003 +#define KEY_SHIFT_OPTN 30059 +#define KEY_CTRL_RESERVE1 30060 +#define KEY_CTRL_RESERVE2 30061 +#define KEY_SHIFT_LEFT 30062 +#define KEY_SHIFT_RIGHT 30063 +#define KEY_UP_CTRL 31060 +#define KEY_DOWN_CTRL 31061 +#define KEY_LEFT_CTRL 31062 +#define KEY_RIGHT_CTRL 31063 +#define KEY_CALCULATOR 31064 +#define KEY_SAVE 31065 +#define KEY_LOAD 31066 +#define KEY_CTRL_A 31001 +#define KEY_CTRL_D 31004 +#define KEY_CTRL_E 31005 +#define KEY_CTRL_H 31008 // help? +#define KEY_CTRL_M 31011 // doc menu +#define KEY_CTRL_N 31012 +#define KEY_CTRL_R 31018 +#define KEY_CTRL_S 31019 +#define KEY_CTRL_T 31020 +#define KEY_EQW_TEMPLATE 31100 +#define KEY_AFFECT 31101 +#define KEY_FLAG 31102 +#define KEY_BOOK 31103 +//#define KEY_CTRL_APPS 31104 +#define KEY_CTRL_SYMB 31105 +//#define KEY_CTRL_NUM 31106 +//#define KEY_CTRL_PLOT 31107 +#define KEY_SELECT_LEFT 31200 +#define KEY_SELECT_UP 31201 +#define KEY_SELECT_DOWN 31202 +#define KEY_SELECT_RIGHT 31203 +#define KEY_SHUTDOWN 32109 + +#define KEY_PRGM_ACON 10 +#define KEY_PRGM_DOWN 37 +#define KEY_PRGM_EXIT 47 +#define KEY_PRGM_F1 79 +#define KEY_PRGM_F2 69 +#define KEY_PRGM_F3 59 +#define KEY_PRGM_F4 49 +#define KEY_PRGM_F5 39 +#define KEY_PRGM_F6 29 +#define KEY_PRGM_LEFT 38 +#define KEY_PRGM_NONE 0 +#define KEY_PRGM_RETURN 31 +#define KEY_PRGM_RIGHT 27 +#define KEY_PRGM_UP 28 +#define KEY_PRGM_1 72 +#define KEY_PRGM_2 62 +#define KEY_PRGM_3 52 +#define KEY_PRGM_4 73 +#define KEY_PRGM_5 63 +#define KEY_PRGM_6 53 +#define KEY_PRGM_7 74 +#define KEY_PRGM_8 64 +#define KEY_PRGM_9 54 +#define KEY_PRGM_A 76 +#define KEY_PRGM_F 26 +#define KEY_PRGM_ALPHA 77 +#define KEY_PRGM_SHIFT 78 +#define KEY_PRGM_MENU 48 +#define KEY_CTRL_SD 39990 + +#endif diff --git a/ion/src/simulator/nspire/keyboard.cpp b/ion/src/simulator/nspire/keyboard.cpp new file mode 100644 index 00000000000..65df840a99e --- /dev/null +++ b/ion/src/simulator/nspire/keyboard.cpp @@ -0,0 +1,276 @@ +#include +#include + + +#include +#include +#include + +#include "layout_keyboard.h" +#include "main.h" +#include "k_csdk.h" + + +using namespace Ion::Keyboard; + +class KeyPair { +public: + constexpr KeyPair(Key key, bool numworksShift, bool numworksAlpha, int gintKey, bool gintShift, bool gintAlpha, bool ignoreShiftAlpha = false) : + m_key(key), + m_numworksShift(numworksShift), + m_numworksAlpha(numworksAlpha), + m_gintKey(gintKey), + m_gintShift(gintShift), + m_gintAlpha(gintAlpha), + m_ignoreShiftAlpha(ignoreShiftAlpha) + {} + Key key() const { return m_key; } + bool numworksShift() const { return m_numworksShift; } + bool numworksAlpha() const { return m_numworksAlpha; } + int gintKey() const { return m_gintKey; } + bool gintShift() const { return m_gintShift; } + bool gintAlpha() const { return m_gintAlpha; } + bool ignoreShiftAlpha() const { return m_ignoreShiftAlpha; } +private: + Key m_key; + bool m_numworksShift; + bool m_numworksAlpha; + int m_gintKey; + bool m_gintShift; + bool m_gintAlpha; + bool m_ignoreShiftAlpha; +}; + +constexpr static KeyPair sKeyPairs[] = { + KeyPair(Key::Down, false, false, KEY_DOWN, false, false, true), + KeyPair(Key::Left, false, false, KEY_LEFT, false, false, true), + KeyPair(Key::Right, false, false, KEY_RIGHT, false, false, true), + KeyPair(Key::Up, false, false, KEY_UP, false, false, true), + KeyPair(Key::Back, false, false, KEY_EXIT, false, false), + KeyPair(Key::Home, false, false, KEY_MENU, false, false), + KeyPair(Key::Shift, false, false, KEY_SHIFT, false, false, true), + KeyPair(Key::Alpha, false, false, KEY_ALPHA, false, false, true), + KeyPair(Key::XNT, false, false, KEY_XOT, false, false), + KeyPair(Key::Var, false, false, KEY_VARS, false, false), + KeyPair(Key::Toolbox, false, false, KEY_CTRL_CATALOG, false, false), + KeyPair(Key::Backspace, false, false, KEY_DEL, false, false), + KeyPair(Key::Exp, false, false, KEY_CHAR_EXPN, false, false), + KeyPair(Key::Ln, false, false, KEY_LN, false, false), + KeyPair(Key::Log, false, false, KEY_LOG, false, false), + KeyPair(Key::Imaginary, false, false, KEY_CHAR_IMGNRY, false, false), + KeyPair(Key::Comma, false, false, KEY_COMMA, false, false), + KeyPair(Key::Power, false, false, '^', false, false), + KeyPair(Key::Sine, false, false, KEY_SIN, false, false), + KeyPair(Key::Cosine, false, false, KEY_COS, false, false), + KeyPair(Key::Tangent, false, false, KEY_TAN, false, false), + KeyPair(Key::Pi, false, false, KEY_CHAR_PI, false, false), + KeyPair(Key::Sqrt, false, false, KEY_CHAR_ROOT, false, false), + KeyPair(Key::Square, false, false, KEY_SQUARE, false, false), + KeyPair(Key::Seven, false, false, KEY_7, false, false), + KeyPair(Key::Eight, false, false, KEY_8, false, false), + KeyPair(Key::Nine, false, false, KEY_9, false, false), + KeyPair(Key::LeftParenthesis, false, false, KEY_LEFTP, false, false), + KeyPair(Key::RightParenthesis, false, false, KEY_RIGHTP, false, false), + KeyPair(Key::Four, false, false, KEY_4, false, false), + KeyPair(Key::Five, false, false, KEY_5, false, false), + KeyPair(Key::Six, false, false, KEY_6, false, false), + KeyPair(Key::Multiplication, false, false, KEY_MUL, false, false), + KeyPair(Key::Division, false, false, KEY_DIV, false, false), + KeyPair(Key::Division, false, false, KEY_FRAC, false, false), + KeyPair(Key::One, false, false, KEY_1, false, false), + KeyPair(Key::Two, false, false, KEY_2, false, false), + KeyPair(Key::Three, false, false, KEY_3, false, false), + KeyPair(Key::Plus, false, false, KEY_ADD, false, false), + KeyPair(Key::Minus, false, false, KEY_SUB, false, false), + KeyPair(Key::Zero, false, false, KEY_0, false, false), + KeyPair(Key::Dot, false, false, KEY_DOT, false, false), + KeyPair(Key::EE, false, false, KEY_CHAR_EXP, false, false), + KeyPair(Key::Ans, false, false, KEY_CHAR_ANS, true, false), + KeyPair(Key::EXE, false, false, KEY_EXE, false, false, true), + KeyPair(Key::OnOff, false, false, KEY_ACON, false, false), + + // Cut + KeyPair(Key::XNT, true, false, KEY_CTRL_CUT, false, false), + // Copy + KeyPair(Key::Var, true, false, KEY_CTRL_CLIP, false, false), + // Paste + KeyPair(Key::Toolbox, true, false, KEY_CTRL_PASTE, false, false), + // Clear + KeyPair(Key::Backspace, true, false, KEY_ACON, false, false), + // [ + KeyPair(Key::Exp, true, false, KEY_CHAR_CROCHETS, false, false), + // ] + KeyPair(Key::Ln, true, false, KEY_CTRL_F13, false, false), + // { + KeyPair(Key::Log, true, false, KEY_CHAR_ACCOLADES, false, false), + // } + KeyPair(Key::Imaginary, true, false, KEY_CTRL_F14, false, false), + // _ + KeyPair(Key::Comma, true, false, KEY_NEG, false, false), + // -> + KeyPair(Key::Power, true, false, KEY_STORE, false, true), + // asin + KeyPair(Key::Sine, true, false, KEY_CHAR_ASIN, true, false), + // acos + KeyPair(Key::Cosine, true, false, KEY_CHAR_ACOS, true, false), + // atan + KeyPair(Key::Tangent, true, false, KEY_CHAR_ATAN, true, false), + // = + KeyPair(Key::Pi, true, false, KEY_CHAR_EQUAL, false, false), + // < + KeyPair(Key::Sqrt, true, false, '<', false, false), + // > + KeyPair(Key::Square, true, false, '>', false, false), + + // : + KeyPair(Key::XNT, false, true, ':', false, false), + // ; + KeyPair(Key::Var, false, true, ';', false, false), + // " + KeyPair(Key::Toolbox, false, true, KEY_CHAR_DQUATE, false, true), + // % + KeyPair(Key::Backspace, false, true, '%', false, false), + // A + KeyPair(Key::Exp, false, true, 'a', false, false), + // B + KeyPair(Key::Ln, false, true, 'b', false, false), + // C + KeyPair(Key::Log, false, true, 'c', false, false), + // D + KeyPair(Key::Imaginary, false, true, 'd', false, false), + // E + KeyPair(Key::Comma, false, true, 'e', false, false), + // F + KeyPair(Key::Power, false, true, 'f', false, false), + // G + KeyPair(Key::Sine, false, true, 'g', false, false), + // H + KeyPair(Key::Cosine, false, true, 'h', false, false), + // I + KeyPair(Key::Tangent, false, true, 'i', false, false), + // J + KeyPair(Key::Pi, false, true, 'j', false, false), + // K + KeyPair(Key::Sqrt, false, true,'k', false, false), + // L + KeyPair(Key::Square, false, true, 'l', false, false), + // M + KeyPair(Key::Seven, false, true, 'm', false, false), + // N + KeyPair(Key::Eight, false, true, 'n', false, false), + // O + KeyPair(Key::Nine, false, true, 'o', false, false), + // P + KeyPair(Key::LeftParenthesis, false, true, 'p', false, false), + // Q + KeyPair(Key::RightParenthesis, false, true, 'q', false, false), + // R + KeyPair(Key::Four, false, true, 'r', false, false), + // S + KeyPair(Key::Five, false, true, 's', false, false), + // T + KeyPair(Key::Six, false, true, 't', false, false), + // U + KeyPair(Key::Multiplication, false, true, 'u', false, false), + // V + KeyPair(Key::Division, false, true, 'v', false, false), + // W + KeyPair(Key::One, false, true, 'w', false, false), + // X + KeyPair(Key::Two, false, true, 'x', false, false), + // Y + KeyPair(Key::Three, false, true, 'y', false, false), + // Z + KeyPair(Key::Plus, false, true, 'z', false, false), + // Space + KeyPair(Key::Minus, false, true, KEY_CHAR_SPACE, false, false), + // ? + KeyPair(Key::Zero, false, true, KEY_F6, true, false), + // ! + KeyPair(Key::Dot, false, true, KEY_F6, false, false), + + // Brightness control shortcut in Upsilon + KeyPair(Key::Plus, true, false, KEY_ADD, false, true), + KeyPair(Key::Minus, true, false, KEY_SUB, false, true), + + +}; + +constexpr int sNumberOfKeyPairs = sizeof(sKeyPairs)/sizeof(KeyPair); + +namespace Ion { +namespace Keyboard { + +int menuHeldFor = 0; + +State scan() { + State state = 0; + + // Grab this opportunity to refresh the display if needed + Simulator::Main::refresh(); + if (!any_key_pressed()) + return state; + if (isKeyPressed(KEY_NSPIRE_HOME)){ + state.setKey(Key::Home); return state; + } + if (isKeyPressed(KEY_NSPIRE_ENTER)){ + state.setKey(Key::EXE); + return state; + } + if (isKeyPressed(KEY_NSPIRE_LEFT)){ + state.setKey(Key::Left); + return state; + } + if (isKeyPressed(KEY_NSPIRE_RIGHT)){ + state.setKey(Key::Right); + return state; + } + if (isKeyPressed(KEY_NSPIRE_UP)){ + state.setKey(Key::Up); + return state; + } + if (isKeyPressed(KEY_NSPIRE_DOWN)){ + state.setKey(Key::Down); + return state; + } + int shiftstate; + int scancode=nspire_scan(&shiftstate); + if (scancode==0) { + return state; + } + switch (scancode){ + case -2: case -1: + return state; + case KEY_CTRL_EXIT: + state.setKey(Key::Back); + return state; + case KEY_CTRL_EXE: + state.setKey(Key::EXE); + return state; + case KEY_CTRL_MENU: + state.setKey(Key::Home); + return state; + } + nspire_ctrl=nspire_shift=0; + for (int i = 0; i < sNumberOfKeyPairs; i++) { + const KeyPair & keyPair = sKeyPairs[i]; + if (scancode==keyPair.gintKey()) { + state.setSimulatedShift(keyPair.numworksShift() ? ModSimState::ForceOn : ModSimState::ForceOff); + state.setSimulatedAlpha(keyPair.numworksAlpha() ? ModSimState::ForceOn : ModSimState::ForceOff); + state.setKey(keyPair.key()); + return state; + } + } + return state; +} + +} +} + +namespace Ion { +namespace Simulator { +namespace Keyboard { + +} +} +} diff --git a/ion/src/simulator/nspire/keyboard.h b/ion/src/simulator/nspire/keyboard.h new file mode 100644 index 00000000000..0e379642fb8 --- /dev/null +++ b/ion/src/simulator/nspire/keyboard.h @@ -0,0 +1,15 @@ +#ifndef ION_SIMULATOR_KEYBOARD_H +#define ION_SIMULATOR_KEYBOARD_H + +#include +// #include + +namespace Ion { +namespace Simulator { +namespace Keyboard { + +} +} +} + +#endif \ No newline at end of file diff --git a/ion/src/simulator/nspire/main.cpp b/ion/src/simulator/nspire/main.cpp new file mode 100644 index 00000000000..4ed2bafd471 --- /dev/null +++ b/ion/src/simulator/nspire/main.cpp @@ -0,0 +1,97 @@ +#include "main.h" +#include "display.h" +#include "platform.h" + + +#include +#include +#include +#include +#include + +#include +#include +#include "k_csdk.h" + +static const char * storage_name="nwstore.nws"; + +int save_state(const char * fname); // apps/home/controller.cpp + +extern "C" { + extern const int prizm_heap_size; + const int prizm_heap_size=1024*1024; + __attribute__((aligned(4))) char prizm_heap[prizm_heap_size]; + + int calculator=4; // -1 means OS not checked, 0 unknown, 1 cg50 or 90, 2 emu 50 or 90, 3 other + + int main() { + sdk_init(); + Ion::Simulator::Main::init(); + ion_main(0, NULL); + Ion::Simulator::Main::quit(); + sdk_end(); + return 0; + } +} + +namespace Ion { +namespace Simulator { +namespace Main { + +static bool sNeedsRefresh = false; + +void init() { + Ion::Simulator::Display::init(); + setNeedsRefresh(); +} + +void setNeedsRefresh() { + sNeedsRefresh = true; +} + +void refresh() { + if (!sNeedsRefresh) { + return; + } + + Display::draw(); + + sNeedsRefresh = false; +} + +void quit() { + Ion::Simulator::Display::quit(); +} + +void runPowerOffSafe(void (*powerOffSafeFunction)(), bool prepareVRAM) { + // somewhat OFF by setting LCD to 0 + unsigned NSPIRE_CONTRAST_ADDR=is_cx2?0x90130014:0x900f0020; + unsigned oldval=*(volatile unsigned *)NSPIRE_CONTRAST_ADDR,oldval2; + if (is_cx2){ + oldval2=*(volatile unsigned *) (NSPIRE_CONTRAST_ADDR+4); + *(volatile unsigned *) (NSPIRE_CONTRAST_ADDR+4)=0xffff; + } + *(volatile unsigned *)NSPIRE_CONTRAST_ADDR=is_cx2?0xffff:0x100; + static volatile uint32_t *lcd_controller = (volatile uint32_t*) 0xC0000000; + lcd_controller[6] &= ~(0b1 << 11); + loopsleep(20); + lcd_controller[6] &= ~ 0b1; + unsigned NSPIRE_RTC_ADDR=0x90090000; + unsigned offtime=* (volatile unsigned *) NSPIRE_RTC_ADDR; + for (int n=0;!on_key_pressed();++n){ + loopsleep(100); + idle(); + } + lcd_controller[6] |= 0b1; + loopsleep(20); + lcd_controller[6]|= 0b1 << 11; + if (is_cx2) + *(volatile unsigned *)(NSPIRE_CONTRAST_ADDR+4)=oldval2; + *(volatile unsigned *)NSPIRE_CONTRAST_ADDR=oldval; + sync_screen(); + +} + +} +} +} diff --git a/ion/src/simulator/nspire/main.h b/ion/src/simulator/nspire/main.h new file mode 100644 index 00000000000..4ac441a065f --- /dev/null +++ b/ion/src/simulator/nspire/main.h @@ -0,0 +1,20 @@ +#ifndef ION_SIMULATOR_MAIN_H +#define ION_SIMULATOR_MAIN_H + +namespace Ion { +namespace Simulator { +namespace Main { + +void init(); +void quit(); + +void setNeedsRefresh(); +void refresh(); + +void runPowerOffSafe(void (*powerOffSafeFunction)(), bool prepareVRAM); + +} +} +} + +#endif \ No newline at end of file diff --git a/ion/src/simulator/nspire/platform.h b/ion/src/simulator/nspire/platform.h new file mode 100644 index 00000000000..af5ddfcb60a --- /dev/null +++ b/ion/src/simulator/nspire/platform.h @@ -0,0 +1,23 @@ +#ifndef ION_SIMULATOR_PLATFORM_H +#define ION_SIMULATOR_PLATFORM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Those functions should be implemented per-platform. + * They are defined as C function for easier interop. */ + +const char * IonSimulatorGetLanguageCode(); + +void IonSimulatorKeyboardKeyDown(int keyNumber); +void IonSimulatorKeyboardKeyUp(int keyNumber); +void IonSimulatorEventsPushEvent(int eventNumber); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/ion/src/simulator/nspire/power.cpp b/ion/src/simulator/nspire/power.cpp new file mode 100644 index 00000000000..e14daa6a88f --- /dev/null +++ b/ion/src/simulator/nspire/power.cpp @@ -0,0 +1,23 @@ +#include +#include +#include + +#include "main.h" + +void powerOff(void){ +} + + +namespace Ion { +namespace Power { + +void suspend(bool checkIfOnOffKeyReleased) { + Simulator::Main::runPowerOffSafe(powerOff, true); +} + +void standby() { + Simulator::Main::runPowerOffSafe(powerOff, true); +} + +} +} diff --git a/ion/src/simulator/nspire/telemetry_init.cpp b/ion/src/simulator/nspire/telemetry_init.cpp new file mode 100644 index 00000000000..7a69b2d8c83 --- /dev/null +++ b/ion/src/simulator/nspire/telemetry_init.cpp @@ -0,0 +1,15 @@ +#include "platform.h" + +namespace Ion { +namespace Simulator { +namespace Telemetry { + +void init() { +} + +void shutdown() { +} + +} +} +} \ No newline at end of file diff --git a/ion/src/simulator/nspire/timing.cpp b/ion/src/simulator/nspire/timing.cpp new file mode 100644 index 00000000000..9378fe1c0f5 --- /dev/null +++ b/ion/src/simulator/nspire/timing.cpp @@ -0,0 +1,21 @@ +#include +#include +#include "k_csdk.h" + +namespace Ion { +namespace Timing { + +uint64_t millis() { + return ::millis(); +} + +void usleep(uint32_t us) { + os_wait_1ms(us/1000); // sleep_us(us); +} + +void msleep(uint32_t ms) { + os_wait_1ms(ms); // sleep_us(ms * 1000); +} + +} +} diff --git a/liba/include/bridge/string.h b/liba/include/bridge/string.h index 51d6a8c3a34..790fd6b7ebd 100644 --- a/liba/include/bridge/string.h +++ b/liba/include/bridge/string.h @@ -7,9 +7,10 @@ LIBA_BEGIN_DECLS -#if (__GLIBC__ || __MINGW32__ || _FXCG) +#if (__GLIBC__ || __MINGW32__ || _FXCG || NSPIRE_NEWLIB) size_t strlcat(char * dst, const char * src, size_t dstSize); size_t strlcpy(char * dst, const char * src, size_t len); +char *strdup(const char *s); #endif LIBA_END_DECLS diff --git a/liba/include/bridge/strings.h b/liba/include/bridge/strings.h index d74b264db18..591eec09c6c 100644 --- a/liba/include/bridge/strings.h +++ b/liba/include/bridge/strings.h @@ -1,7 +1,7 @@ #ifndef LIBA_STRINGS_H #define LIBA_STRINGS_H -#if (_FXCG) +#if (_FXCG) || defined NSPIRE_NEWLIB #include @@ -19,4 +19,4 @@ LIBA_END_DECLS #endif -#endif \ No newline at end of file +#endif diff --git a/libaxx/include/bridge/cmath b/libaxx/include/bridge/cmath index 11eec165e87..a267e5f672e 100644 --- a/libaxx/include/bridge/cmath +++ b/libaxx/include/bridge/cmath @@ -17,7 +17,7 @@ #define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ #define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ -#if (_FXCG) +#if (_FXCG) || defined NSPIRE_NEWLIB namespace std { // functions using ::acosh; diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 4ac1ba70774..0a5e5b5723e 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -135,7 +135,7 @@ class Expression : public TreeHandle { Expression() : TreeHandle() {} Expression clone() const; static Expression Parse(char const * string, Context * context, bool addMissingParenthesis = true); - static Expression ExpressionFromAddress(const void * address, size_t size); + static Expression ExpressionFromAddress(const void * address, size_t size, const void * record=nullptr); /* Circuit breaker */ typedef bool (*CircuitBreaker)(); diff --git a/poincare/include/poincare/integer.h b/poincare/include/poincare/integer.h index cfc98203495..6ff1dbc1ef2 100644 --- a/poincare/include/poincare/integer.h +++ b/poincare/include/poincare/integer.h @@ -21,7 +21,7 @@ class LayoutNode; class Integer; struct IntegerDivision; -#if (defined _3DS) || (defined _FXCG) +#if (defined _3DS) || (defined _FXCG) || defined NSPIRE_NEWLIB typedef unsigned short half_native_uint_t; static_assert(sizeof(half_native_uint_t) == sizeof(uint16_t)); typedef int native_int_t; diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 4c066e6c1eb..575a8859751 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -38,10 +38,42 @@ Expression Expression::Parse(char const * string, Context * context, bool addPar return expression; } -Expression Expression::ExpressionFromAddress(const void * address, size_t size) { + Expression Expression::ExpressionFromAddress(const void * address, size_t size, const void * record) { if (address == nullptr || size == 0) { return Expression(); } +#ifdef STRING_STORAGE + // Check that expression was stored as a string in record + size_t i; + const char * ptr=(const char *) address; + for (i=1;i 0 && i < size && ptr[i] == '"') { + ((char *)ptr)[i] = 0; + Expression e = Expression::Parse(ptr + 1, nullptr); + ((char *)ptr)[i] = '"'; + address = e.addressInPool(); + size = e.size(); + if (record) { + const char * name = ((const Ion::Storage::Record *)record)->fullName(); + char repl = 0; + int l = strlen(name); + if (strncmp(name+l-4,".seq",4) == 0) { + repl='n'; + } else if (strncmp(name+l-5,".func",5) == 0) { + repl='x'; + } + if (repl){ + e=e.replaceSymbolWithExpression(Symbol::Builder(repl),Symbol::Builder(UCodePointUnknown)); + address= e.addressInPool(); + size=e.size(); + } + } + return Expression(static_cast(TreePool::sharedPool()->copyTreeFromAddress(address, size))); // must be done before e is destroyed + } +#endif // Build the Expression in the Tree Pool return Expression(static_cast(TreePool::sharedPool()->copyTreeFromAddress(address, size))); } diff --git a/python/port/port.cpp b/python/port/port.cpp index 67c57ef3f9d..07b2e04d320 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -41,6 +41,25 @@ void python_error_end() { } #endif +#if defined _FXCG || defined NSPIRE_NEWLIB +#ifdef _FXCG +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* py/parsenum.h is a C header which uses C keyword restrict. * It does not exist in C++ so we define it here in order to be able to include @@ -354,23 +373,119 @@ void nlr_jump_fail(void *val) { while (1); } + +#if defined _FXCG || defined NSPIRE_NEWLIB +void do_mp_lexer_new_from_file(const char * filename,mp_lexer_t ** res) { + mp_reader_t reader; + mp_reader_new_file(&reader, filename); + *res=mp_lexer_new(qstr_from_str(filename), reader); +} + +// Code from MicroPython's reader.c +#include +#include +#include + +typedef struct _mp_reader_posix_t { + bool close_fd; + int fd; + size_t len; + size_t pos; + byte buf[20]; +} mp_reader_posix_t; + +STATIC mp_uint_t mp_reader_posix_readbyte(void *data) { + mp_reader_posix_t *reader = (mp_reader_posix_t *)data; + if (reader->pos >= reader->len) { + if (reader->len == 0) { + return MP_READER_EOF; + } else { + MP_THREAD_GIL_EXIT(); + int n = read(reader->fd, reader->buf, sizeof(reader->buf)); + MP_THREAD_GIL_ENTER(); + if (n <= 0) { + reader->len = 0; + return MP_READER_EOF; + } + reader->len = n; + reader->pos = 0; + } + } + return reader->buf[reader->pos++]; +} + +STATIC void mp_reader_posix_close(void *data) { + mp_reader_posix_t *reader = (mp_reader_posix_t *)data; + if (reader->close_fd) { + MP_THREAD_GIL_EXIT(); + close(reader->fd); + MP_THREAD_GIL_ENTER(); + } + m_del_obj(mp_reader_posix_t, reader); +} + +void mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd) { + mp_reader_posix_t *rp = m_new_obj(mp_reader_posix_t); + rp->close_fd = close_fd; + rp->fd = fd; + MP_THREAD_GIL_EXIT(); + int n = read(rp->fd, rp->buf, sizeof(rp->buf)); + if (n == -1) { + if (close_fd) { + close(fd); + } + MP_THREAD_GIL_ENTER(); + mp_raise_OSError(errno); + } + MP_THREAD_GIL_ENTER(); + rp->len = n; + rp->pos = 0; + reader->data = rp; + reader->readbyte = mp_reader_posix_readbyte; + reader->close = mp_reader_posix_close; +} +void mp_reader_new_file(mp_reader_t *reader, const char *filename) { + MP_THREAD_GIL_EXIT(); + int fd = open(filename, O_RDONLY, 0644); + MP_THREAD_GIL_ENTER(); + if (fd < 0) { + mp_raise_OSError(errno); + } + mp_reader_new_file_from_fd(reader, fd, true); +} +#endif + mp_lexer_t * mp_lexer_new_from_file(const char * filename) { if (sScriptProvider != nullptr) { const char * script = sScriptProvider->contentOfScript(filename, true); if (script != nullptr) { return mp_lexer_new_from_str_len(qstr_from_str(filename), script, strlen(script), 0 /* size_t free_len*/); - } else { - mp_raise_OSError(MP_ENOENT); } - } else { - mp_raise_OSError(MP_ENOENT); } +#ifdef _FXCG + mp_lexer_t * res=0; + gint_world_switch(GINT_CALL(do_mp_lexer_new_from_file,filename,&res)); + return res; +#endif +#ifdef NSPIRE_NEWLIB + mp_lexer_t * res=0; + do_mp_lexer_new_from_file(filename,&res); + return res; +#endif + mp_raise_OSError(MP_ENOENT); } mp_import_stat_t mp_import_stat(const char *path) { if (sScriptProvider && sScriptProvider->contentOfScript(path, false)) { return MP_IMPORT_STAT_FILE; } +#if defined _FXCG || defined NSPIRE_NEWLIB + FILE * f=fopen(path,"rb"); + if (f) { + fclose(f); + return MP_IMPORT_STAT_FILE; + } +#endif return MP_IMPORT_STAT_NO_EXIST; }