/****************************************************************************** * * * A M S E x t e n d e r * * * * by Stefan Heule * * member of boolsoft (www.boolsoft.org) * * * * Source Code * * * * File: hook.c * * Use: - source code file * * - main routine of the hook * * * ******************************************************************************* AMS Extender - A utility for TI's 68k calculaters Copyright (C) 2006-2008 Stefan Heule This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ // ----------------------------------------------------------------- // // -----------------------[ custom events ]------------------------- // // ----------------------------------------------------------------- // #define CUSTOM_CM_BACKUP_SETTINGS_ALL_TSR 0x6000 #define CUSTOM_CM_RESTORE_SETTINGS_ALL_TSR 0x6001 #define CUSTOM_CM_BACKUP_SETTINGS_AMSEXT 0x6002 #define CUSTOM_CM_RESTORE_SETTINGS_AMSEXT 0x6003 #define CUSTOM_CM_BACKUP_SETTINGS_EASYCHAR 0x6004 #define CUSTOM_CM_RESTORE_SETTINGS_EASYCHAR 0x6005 // ----------------------------------------------------------------- // // ---------------------------[ defines ]--------------------------- // // ----------------------------------------------------------------- // #define _NO_INCLUDE_PATCH // smaller code #define OPTIMIZE_ROM_CALLS #define NO_CALC_DETECT // minimal ams version #define MIN_AMS 200 // makro by Kevin Koflor to detect the max number of items in the history #define HS_MaxExpressions (*((unsigned short*)(_rom_call_addr_hack(5DE,\ ((unsigned short *)(long)*((short *)_rom_call_addr(23C)+((AMS_1xx)?3:2))),204)))) // if you change the backup struct, change this macig, too! // for a description of the meaning see the file install.c #define OPTIONS_FILE_MAGIC "amsext02" // name of the backup file #define OPTIONS_FILE_NAME "amsxtcfg" typedef struct _options { unsigned short keydelay; // key delay, delay in 1/20 second unsigned short scrolldelay; // scroll delay, delay in 1/20 second unsigned long cursor_rate; // cursor blink delay, delay in 1/20 second unsigned long apd; // auto power down, 20 * second short auto_add_brackets; // automatically add brackets, 0: off, 1: on unsigned char clock_type; // clock icon, 0: off, 1: 12h, 2: 24h unsigned char battery_type; // battery icon, 0: off, 1: on unsigned char keyboard_layout; // keyboard layout, 0: QWERTY, 1: QWERTZ unsigned char autostart_prgm[18]; // autostart programm, 'name'+'\\'+'folder' or 'name' unsigned char _2nd_MEM[18]; // programm to be executed when 2nd+MEM is pressed unsigned char _2nd_CHAR[18]; // programm to be executed when 2nd+CHAR is pressed unsigned char off_by_on; // turn calc off by pressing on, 0: off, 1: on } OPTIONS; // the preferences OPTIONS pref; // ----------------------------------------------------------------- // // ------------------------[ header files ]------------------------- // // ----------------------------------------------------------------- // #include #include "messages.h" // ----------------------------------------------------------------- // // -------------------------[ prototypes ]-------------------------- // // ----------------------------------------------------------------- // // trs-routines void Install_TSR(void); void EventHandler(EVENT *ev); // own routines void Limits(void); CALLBACK short Callback(short Message, long Value); CALLBACK HANDLE PopupCallback(short ID); void PrintTime(const char* time); void PrintBattery(char* time); void RunApplication(const unsigned char* name, short special); SYM_ENTRY *FindFile (const char *filename, char *folder_name); TEXT_EDIT *TE_findHomeScreen(void); unsigned short GetBatteryState(void); void loadBackup(void); void default_all_settings(OPTIONS* pref_); void applyConfig(void); unsigned char checksum(void* data, unsigned short bytes); // time (documented by Olivier Armand (ExtendeD)) #define DateAndTime_Get _rom_call(void,(unsigned short*,unsigned short*,unsigned short*,\ unsigned short*,unsigned short*,unsigned short*),5F3) // ----------------------------------------------------------------- // // -------------------------[ global vars ]------------------------- // // ----------------------------------------------------------------- // // home entry line TEXT_EDIT *entryLine; const char *entryLineText; // app id's short program_editor_id; short text_editor_id; short home_id; short matrix_editor_id; // variable to detect key combos #if defined(USE_TI89) unsigned short prev_flags; unsigned short flags; #endif // ----------------------------------------------------------------- // // ------------------------[ hook header ]-------------------------- // // ----------------------------------------------------------------- // // total length of this header is 22 bytes // offset to jmp EntryPoint:l is 16 bytes // offset to gpOldHandler is 12 bytes asm(".section _stl1 | First library startup section. This is what the executable starts with. .ascii \"evHk" TSR_NAME "\" .globl gpOldHandler gpOldHandler: .long 0 __jmp__Event_Hook__: jmp Install_TSR:l"); #ifndef NO_CALC_DETECT asm(".global __calculator __calculator: .word 0"); #endif //a pointer to a pointer to the old handler extern EVENT_HANDLER gpOldHandler; // -------------------------[ Install_TSR ]------------------------- // // Installs the event hook // // ----------------------------------------------------------------- // // note: // // Will be executed once, only once! // // ----------------------------------------------------------------- // void Install_TSR(void) { extern char __jmp__Event_Hook__; char *ptr=&__jmp__Event_Hook__; // set EventHandler as the default entry point from now on // instead of Install_TSR (this function) *(unsigned long*)&ptr[2]=(unsigned long)EventHandler; #ifndef NO_CALC_DETECT asm volatile(" /* Start Of Calculator Detection Code -- From TIGCC Sources */ move.l 0xC8,%%a0 /* Calculator Detection */ moveq #1,%%d0 move.l %%a0,%%d1 and.l #0x400000,%%d1 jbne __calc_in_d0__ clr.w %%d0 move.l (%%a0,0x2F*4),%%a1 /* ScrRect */ cmp.b #200,2(%%a1) jbcs __calc_in_d0__ moveq #3,%%d0 __calc_in_d0__: lea __calculator(%%pc),%%a1 move.w %%d0,(%%a1) /* End Of Calculator Detection Code -- From TIGCC Sources */" : : : "a0", "a1", "d1"); #endif // one time initialisation: default_all_settings(&pref); loadBackup(); Limits(); applyConfig(); // get a pointer to the home screen command line entryLine = TE_findHomeScreen(); // get the app id's program_editor_id = TIOS_EV_getAppID("TIPRGMED"); text_editor_id = TIOS_EV_getAppID("TITEXTED"); home_id = TIOS_EV_getAppID("TIHOME"); matrix_editor_id = TIOS_EV_getAppID("TIDMED"); } // ------------------------[ EventHandler ]------------------------- // // Event hook; handles all the events // // ----------------------------------------------------------------- // // input: // // ev = pointer to the event to be processed // // local: // // OldHook = pointer to the old event hook // // ----------------------------------------------------------------- // void EventHandler(EVENT *ev) { // Prepatory Setup for the Event Handler EVENT_HANDLER OldHook; #ifdef OPTIMIZE_ROM_CALLS void *saved_a5; // a variable to save the register a5 so it can be restored later saved_a5 = __jmp_tbl; __jmp_tbl = *(void**)0xC8; #endif #ifdef USE_V200 unsigned char time_buffer[10]; // used for the clock unsigned short unused, ho, mi; // time variables unsigned long old = 0; // to update the clock #endif unsigned char auto_add[18]; // used for the auto add brackets feature // handle for popups and similar HANDLE h; // the AMS doesn't shut down the calc anymore, neither by pressing [2nd]+[ON]/[diamond]+[ON] // (this is hooked by AMS Ext), nor with the APD feature. Additionally, lazy people, // such as me, can turn their calc off just by pressing [ON] :) // note: the calc will turn 0.25 sec befor pref.apd realy would expire.. but who cares :) if ((OSTimerCurVal(APD_TIMER) < 5) || (ev->Type == CM_KEYPRESS && ((ev->extra.Key.Code == KEY_ON && pref.off_by_on) || ev->extra.Key.Code == KEY_OFF || ev->extra.Key.Code == KEY_OFF2))) { if (ev->Type == CM_KEYPRESS && (ev->extra.Key.Code == KEY_ON || ev->extra.Key.Code == KEY_OFF || ev->extra.Key.Code == KEY_OFF2)) { ev->Type = CM_NULL; } off(); // restart the apd-timer OSFreeTimer(APD_TIMER); OSRegisterTimer(APD_TIMER, pref.apd); // start the autostart-programm (if there is one) if (pref.autostart_prgm[0]) { RunApplication(pref.autostart_prgm, 0); } } // 2nd + MEM starts a program (if there's one) if (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == KEY_MEM && pref._2nd_MEM[0]) { RunApplication(pref._2nd_MEM, 3); ev->Type = CM_NULL; } // 2nd + MEM starts a program (if there's one) if (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == KEY_CHAR && pref._2nd_CHAR[0]) { RunApplication(pref._2nd_CHAR, 3); ev->Type = CM_NULL; } #if defined(USE_V200) // if the german keyboard layout is set (QWERTZ), we have to change Y and Z if (pref.keyboard_layout) { if (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == 'z') ev->extra.Key.Code = 'y'; else if (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == 'Z') ev->extra.Key.Code = 'Y'; else if (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == 'y') ev->extra.Key.Code = 'z'; else if (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == 'Y') ev->extra.Key.Code = 'Z'; } // update the time if (pref.clock_type && ((old - OSTimerCurVal(APD_TIMER)) > 18 || ((long)(old - OSTimerCurVal(APD_TIMER))) < 0)) { // get the time DateAndTime_Get(&unused, &unused, &unused, &ho, &mi, &unused); // fill with the correct time format if (pref.clock_type == 1) // 12h { sprintf(time_buffer, ho > 12 ? "%02i:%02i PM" : "%02i:%02i AM", ho > 12 ? ho-12 : ho, mi); } else // 24h { sprintf(time_buffer, "%02i:%02i", ho, mi); } old = OSTimerCurVal(APD_TIMER); } // if the user pressed [DIAMOND]+[A], check if we are in the home screen, and then // mark the complete text. this feature simultates the ctrl+a feature on a normal // computer if (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == 'A'+KEY_DIAMOND && EV_currentApp == home_id) { // there has to be text in the command line, and the cursor must not be in the // history (i.e. the command line is focused) if (entryLine->CurSize && (entryLine->Flags & TE_FOCUSED)) { // select the whole text TE_select(entryLine, 0, entryLine->CurSize); } } #endif // [diamond] + [CLEAR] deletes all entries in the home screen // history (i.e. it calls ) if (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == KEY_DIAMOND+KEY_CLEAR && EV_currentApp == home_id && (entryLine->Flags & TE_FOCUSED)) { // run RunApplication(NULL, 1); ST_stack(0, HS_MaxExpressions); } #if defined(USE_V200) // check if there's anything in the area where we want to draw if (!((*(char*)(LCD_MEM+(LCD_HEIGHT)*30-1)) & 0x01) /*ST_BUSY*/ && !(ST_flags & (1LU<<13/*ST_PAUSE?*/ || 1LU<<14/*ST_PAUSE?*/ || 1UL<<17/*ST_BATT_LOW*/ || 1UL<<18/*ST_BATT_REPLACE*/))) { // print the time (if the clock is on) if (pref.clock_type) { PrintTime(time_buffer); } // print the battery state (if it is on) if (pref.battery_type) { PrintBattery(time_buffer); } } #endif #if defined(USE_V200) // if the user pressed [diamond]+[U], start the program editor // if the user pressed [diamond]+[I], start the text editor // if the user pressed [diamond]+[M], start the data/matrix editor if ((ev->Type == CM_KEYPRESS && ev->extra.Key.Code == KEY_DIAMOND+'U' && EV_currentApp != program_editor_id) || (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == KEY_DIAMOND+'M' && EV_currentApp != matrix_editor_id) || (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == KEY_DIAMOND+'I' && EV_currentApp != text_editor_id)) { // display a popup with these options: "Current", "Open", "New" h = PopupNew(ev->extra.Key.Code == KEY_DIAMOND+'U' ? MSG_EDITOR_PROGRAM : (ev->extra.Key.Code == KEY_DIAMOND+'I' ? MSG_EDITOR_TEXT : MSG_EDITOR_MATRIX), 0); if (h) { PopupAddText(h, -1, MSG_EDITOR_CURRENT, 0); PopupAddText(h, -1, MSG_EDITOR_OPEN, 0); PopupAddText(h, -1, MSG_EDITOR_NEW, 0); short choice = PopupDo(h, CENTER, CENTER, 0); short start_typ = 0; HeapFree(h); if (choice) { if (choice == 1) start_typ = AP_START_CURRENT; else if (choice == 2) start_typ = AP_START_OPEN; else if (choice == 3) start_typ = AP_START_NEW; // now start the right application EV_startApp(ev->extra.Key.Code == KEY_DIAMOND+'U' ? program_editor_id : (ev->extra.Key.Code == KEY_DIAMOND+'I' ? text_editor_id : matrix_editor_id), start_typ); } } } #else // if the user pressed [2nd]+[F4], start the program editor // if the user pressed [2nd]+[F5], start the text editor if ((ev->Type == CM_KEYPRESS && ev->extra.Key.Code == KEY_F4 && EV_currentApp != program_editor_id && (prev_flags & ST_2ND)) || (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == KEY_F5 && EV_currentApp != text_editor_id && (prev_flags & ST_2ND))) { // display a popup with these options: "Current", "Open", "New" h = PopupNew(ev->extra.Key.Code == KEY_F4 ? MSG_EDITOR_PROGRAM : MSG_EDITOR_TEXT, 0); if (h) { PopupAddText(h, -1, MSG_EDITOR_CURRENT, 0); PopupAddText(h, -1, MSG_EDITOR_OPEN, 0); PopupAddText(h, -1, MSG_EDITOR_NEW, 0); short choice = PopupDo(h, CENTER, CENTER, 0); short start_typ = 0; HeapFree(h); if (choice) { if (choice == 1) start_typ = AP_START_CURRENT; else if (choice == 2) start_typ = AP_START_OPEN; else if (choice == 3) start_typ = AP_START_NEW; // now start the right application EV_startApp(ev->extra.Key.Code == KEY_DIAMOND+'U' ? program_editor_id : (ev->extra.Key.Code == KEY_DIAMOND+'I' ? text_editor_id : matrix_editor_id), start_typ); } } ev->Type = CM_NULL; } #endif // start the kbdprgmX, if the user pressed [diamond]+[1-9] // NOTE: we completely hook this ASM feature, even in the home screen if (ev->Type == CM_KEYPRESS && ev->extra.Key.Code >= KEY_DIAMOND+'1' && ev->extra.Key.Code <= KEY_DIAMOND+'9') { // execute the program sprintf(auto_add, "kbdprgm%i", ev->extra.Key.Code-KEY_DIAMOND-'0'); RunApplication(auto_add, 0); // now, we set this event to "nothing", so that if we are in the // homescreen the kbdprgmX won't be executed twice ev->Type = CM_NULL; } #if defined(USE_V200) // start the kbdfuncX, if the user pressed [diamond]+[F1-F8] if (ev->Type == CM_KEYPRESS && ev->extra.Key.Code >= KEY_DIAMOND+KEY_F1 && ev->extra.Key.Code <= KEY_DIAMOND+KEY_F8) { // execute the program sprintf(auto_add, "kbdfunc%i", ev->extra.Key.Code-KEY_DIAMOND-KEY_F1+1); RunApplication(auto_add, 0); // now, we set this event to "nothing", so that if we are in the // homescreen the kbdprgmX won't be executed twice ev->Type = CM_NULL; } #endif // if the user pressed enter, check wheter the text // in the command line is an executabel, and if yes, // paste the brackets ("()") if necessary if (pref.auto_add_brackets && ev->Type == CM_KEYPRESS && ev->extra.Key.Code == KEY_ENTER && EV_currentApp == home_id) { // get the text entryLineText = HeapDeref(entryLine->Text.h); // only check for missing brackets if there is text in the command line, // if the size of the text isn't bigger than 17 chars (no executable has // a name bigger than foldername+backslash+filename = 8+1+8 = 17), and if // the command line is "focused" (i.e. the cursor isn't in the home screen // history if (entryLine->CurSize && entryLine->CurSize <= 17 && (entryLine->Flags & TE_FOCUSED)) { memcpy(auto_add, entryLineText, 17); auto_add[17] = 0; // check if there is a foldername, and "delete" it // (FindFile searches anyway through all folders) unsigned char *temp = strstr(auto_add, "\\"); if (temp != NULL) memmove(auto_add, temp+1, 17-(temp - auto_add)); // search through all folders (but just for ASM and BASIC files) if (FindFile(auto_add, NULL) != NULL) { // set the cursor to the last possible position TE_select(entryLine, entryLine->CurSize, entryLine->CurSize); // paste the TE_pasteText(entryLine, "()", 2); } } } #if defined(USE_V200) // this is a feature which is for me very useful, because I often have to // switch between RAD and DEG // [diamond]+[*] switches betwenn RAD and DEG if (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == KEY_DIAMOND+'*') { // I know there is a GRAD-option, but I don't care about this, for // the moment I just need these two options MO_currentOptions(); if (((MO_OPTIONS*)MO_option)->Angle == 1) ((MO_OPTIONS*)MO_option)->Angle = 2; else ((MO_OPTIONS*)MO_option)->Angle = 1; MO_digestOptions(0); MO_notifyModeChange(MO_NOTIFY_ANGLE); } #endif #if defined(USE_V200) // options dialog ([diamond]+[MODE]) if (ev->Type == CM_KEYPRESS && ev->extra.Key.Code == KEY_DIAMOND+KEY_MODE) { #else // options dialog ([diamond]+[HOME]) if ((prev_flags & ST_DIAMOND) && ev->Type == CM_KEYPRESS && ev->extra.Key.Code == 277) { #endif RunApplication(NULL, 2); applyConfig(); } #if defined(USE_TI89) // save the old status flag if (ev->Type == CM_NULL || ev->Type == CM_BLINK) { prev_flags = flags; flags = ev->StatusFlags; } #endif // restore settings if (ev->Type == CUSTOM_CM_RESTORE_SETTINGS_ALL_TSR || ev->Type == CUSTOM_CM_RESTORE_SETTINGS_AMSEXT) { loadBackup(); } // call the old hook, if there was one // get the address of the next TSR in line to be executed NULL if there is none OldHook = gpOldHandler; // call the old hook if there is one to be called if(OldHook) OldHook(ev); #ifdef OPTIMIZE_ROM_CALLS __jmp_tbl = saved_a5; #endif } // -------------------------[ applyConfig ]------------------------- // // Sets all timers, as necessary after the config has changed // // ----------------------------------------------------------------- // // note: // // The cursordelay and the APD timer are set, nothing else // // ----------------------------------------------------------------- // void applyConfig(void) { // set the cursordelay OSFreeTimer(CURSOR_TIMER); OSRegisterTimer(CURSOR_TIMER, pref.cursor_rate); // set the pref.apd timer OSFreeTimer(APD_TIMER); OSRegisterTimer(APD_TIMER, pref.apd); } // -------------------------[ loadBackup ]-------------------------- // // Loads the configuration from a file // // ----------------------------------------------------------------- // // local: // // backup = config structure // // file = file structure // // chcksm = file checksum // // backup_file_magic = file magic // // return: // // TRUE, or, in case of an error, FALSE // // note: // // If there's no config file, a new one with default values will // // be created // // ----------------------------------------------------------------- // void loadBackup(void) { OPTIONS backup; FILES file; unsigned char chcksm; unsigned char backup_file_magic[strlen(OPTIONS_FILE_MAGIC)+1]; // unarchive the file EM_moveSymFromExtMem(SYMSTR(OPTIONS_FILE_NAME), HS_NULL); // open the backup file if (FOpen(OPTIONS_FILE_NAME, &file, FM_READ, "cfg") != FS_OK) { ST_helpMsg(MSG_ERROR_FILE_NOT_FOUND); return; } // read the file magic backup_file_magic[strlen(OPTIONS_FILE_MAGIC)] = 0; if (FRead((void*)backup_file_magic, strlen(OPTIONS_FILE_MAGIC), &file) != FS_OK) { FClose(&file); ST_helpMsg(MSG_ERROR_READ_FROM_FILE); return; } // check file version if (strcmp(OPTIONS_FILE_MAGIC, backup_file_magic) != 0) { FClose(&file); ST_helpMsg(MSG_ERROR_OUTDATED_FILE); return; } // read the file content if (FRead((void*)&backup, sizeof(OPTIONS), &file) != FS_OK) { FClose(&file); ST_helpMsg(MSG_ERROR_READ_FROM_FILE); return; } // read the checksum if (FRead((void*)&chcksm, 1, &file) != FS_OK) { FClose(&file); ST_helpMsg(MSG_ERROR_READ_FROM_FILE); return; } // check checksum if (checksum((void*)&backup, sizeof(OPTIONS)) != chcksm) { FClose(&file); ST_helpMsg(MSG_ERROR_CORRUPTED_FILE); return; } // close the file FClose(&file); pref = backup; // archive the file EM_moveSymToExtMem(SYMSTR(OPTIONS_FILE_NAME), HS_NULL); return; } // --------------------------[ checksum ]--------------------------- // // Calculates a simple 1-byte-checksum of the provided data // // ----------------------------------------------------------------- // // local: // // md5 = MD5 structure // // res = 16 byte MD5 checksum // // return: // // first byte of MD5 checksum // // note: // // only one byte of the 16-byte MD5 checksum will be used // // ----------------------------------------------------------------- // unsigned char checksum(void* data, unsigned short bytes) { unsigned char res[16]; MD5_CTX md5; // get MD5 hash of the data MD5Init(&md5); MD5Update(&md5, data, bytes); MD5Final(res, &md5); // we just use the first byte of the 16 byte long MD5 hash return res[0]; } // ----------------------------[ Limits ]--------------------------- // // Checks for weird values of the configuration // // ----------------------------------------------------------------- // // note: // // Does not check all options, only the timers // // ----------------------------------------------------------------- // void Limits(void) { /*if (pref.keydelay < 2) pref.keydelay = 2; if (pref.keydelay > 600) pref.keydelay = 600; if (pref.scrolldelay < 2) pref.scrolldelay = 2; if (pref.scrolldelay > 200) pref.scrolldelay = 200;*/ if (pref.cursor_rate < 1) pref.cursor_rate = 1; if (pref.cursor_rate > 200) pref.cursor_rate = 200; if (pref.apd < 20) pref.apd = 20; if (pref.apd > 20*20*60) pref.apd = 20*20*60; } #if defined(USE_V200) // --------------------------[ PrintTime ]-------------------------- // // Prints the time in the bottom right corner // // ----------------------------------------------------------------- // // input: // // time = String containing the time // // ----------------------------------------------------------------- // void PrintTime(const char* time) { // set the font to 4x6 short Font = FontSetSys(F_4x6); DrawStr(LCD_WIDTH-1-DrawStrWidth(time, F_4x6), LCD_HEIGHT-5, time, A_REPLACE); // restore the old font FontSetSys(Font); } // ------------------------[ PrintBattery ]------------------------- // // Outputs a small icon showing the battery state // // ----------------------------------------------------------------- // // input: // // time = String containing the time // // note: // // The string containing the time is needed to determine the // // correct position of the battery icon // // ----------------------------------------------------------------- // void PrintBattery(char* time) { // if the clock is off, the icon can be drawn completely right if (pref.clock_type == 0) { time[0] = 0; } // get the battery state (quite fast function!) unsigned short battery_state = GetBatteryState(); static const unsigned char batt_lvl[] = {0,0,1,2,3,5,7,8}; // rect around DrawClipRect(&(WIN_RECT){LCD_WIDTH-1-DrawStrWidth(time, F_4x6)-3-8,LCD_HEIGHT-5,LCD_WIDTH-1-DrawStrWidth(time, F_4x6)-3,LCD_HEIGHT-2}, &(SCR_RECT){{0,0,LCD_WIDTH-1,LCD_HEIGHT-1}}, A_NORMAL); // small rect inside ScrRectFill(&(SCR_RECT){{LCD_WIDTH-1-DrawStrWidth(time, F_4x6)-3-8,LCD_HEIGHT-5,LCD_WIDTH-1-DrawStrWidth(time, F_4x6)-4-(7-batt_lvl[battery_state]),LCD_HEIGHT-2}}, &(SCR_RECT){{0,0,LCD_WIDTH-1,LCD_HEIGHT-1}}, A_NORMAL); // the two pixels that it looks like a battery DrawPix(LCD_WIDTH-1-DrawStrWidth(time, F_4x6)-2, LCD_HEIGHT-4, A_NORMAL); DrawPix(LCD_WIDTH-1-DrawStrWidth(time, F_4x6)-2, LCD_HEIGHT-3, A_NORMAL); } #endif // ---------------------[ TE_findHomeScreen ]----------------------- // // Finds the home screen command line // // ----------------------------------------------------------------- // // local: // // a = temporary variable // // return: // // TEXT_EDIT structure of the home screen command line // // note: // // Orginally written by Samuel Stearley in ASM, then portet to C // // by Greg Dietsche // // ----------------------------------------------------------------- // TEXT_EDIT *TE_findHomeScreen(void) { register void *a = HomeExecute; while (*(unsigned long*)a != (unsigned long)TE_select) a += 2; return (TEXT_EDIT*)(unsigned long)(*(unsigned short*)(a-4)); }; // ---------------------------[ FindFile ]-------------------------- // // Searches the executable file (i.e. ASM or BASIC programs) with // // the name // // ----------------------------------------------------------------- // // input: // // filename = name of the file to be searched // // folder_name = pointer to a char array, in which the folder // // name will be stored (at least 9 bytes width) // // local: // // SymPtr = pointer to the file (SYM_ENTRY) // // FilePtr = pointer to the file // // return: // // SYM_ENTRY structure of the file // // note: // // Orginally written Olivier Armand, but I modified most parts // // fit my needs. // // Searches through all folders. // // Doesn't store a foldername, if is NULL. // // ----------------------------------------------------------------- // SYM_ENTRY *FindFile(const char *filename, char *folder_name) { SYM_ENTRY *SymPtr; char *FilePtr; // scan all the folders and all the files SymPtr = SymFindFirst(NULL, FO_RECURSE); for ( ;SymPtr != NULL; SymPtr = SymFindNext()) { // its a folder, so store its name, and continue searching if (SymPtr->flags.bits.folder == 1) { if (folder_name) strcpy(folder_name, SymPtr->name); // store its name continue; } // it's a file, so compare the name if (strcmp (SymPtr->name, filename)) // not the right name, continue searching continue; // correct name, check wheter it is executable FilePtr = (char*)HToESI (SymPtr->handle); if ((*FilePtr == (char)ASM_TAG) || (*FilePtr == (char)FUNC_TAG)) break; // yes, we have found a matching file (return SymPtr) } return SymPtr; } // -----------------------[ RunApplication ]------------------------ // // Runs the file called , or executes a special command // // ----------------------------------------------------------------- // // input: // // filename = name of the file to be executed, or NULL, if // // a special command should be called. // // special = 0: no meaning, will be called // // filename does NOT contain a folder, the // // folder will be added automatically // // 1: 'ClrHome' will be called // // 2: 'amsext("c")' will be called // // 3: calls , but does not add a folder // // local: // // FileSymPtr = pointer to the file (SYM_ENTRY) // // SimpleCommand = command itself, in the following form: // // "foldername\filename()\0" = 8+1+8+2+1 // // "foldername\amsext("c")\0" ) 8+1+6+2+3+1 = 21 // // CommandHandle = handle of the command // // TopEstackPtr = estack pointer // // TopEstackBckp = estack backup // // note: // // Orginally written Olivier Armand, but I modified most parts // // fit my needs. // // ----------------------------------------------------------------- // void RunApplication(const unsigned char* name, short special) { SYM_ENTRY *FileSymPtr; char SimpleCommand[21]; HANDLE CommandHandle; // handle of the command ESI *TopEstackPtr; // estack pointer ESI TopEstackBckp; // estack backup TopEstackPtr = &top_estack; TopEstackBckp = *TopEstackPtr; if (special == 0) { // search for TI-BASIC and ASM if (!(FileSymPtr = FindFile(name, SimpleCommand))) { // the file was not found ST_helpMsg(MSG_ERROR_FILE_NOT_FOUND); return; } // file in use? if (FileSymPtr->flags.bits.hidden) { // the file was not found ST_helpMsg(MSG_ERROR_FILE_IN_USE); return; } // SimpleCommand contains now the foldername, so add.. strcat(SimpleCommand, "\\"); // .. the backslash ("\") strcat(SimpleCommand, name); // .. the filename (max 8 chars) strcat(SimpleCommand, "()"); // .. the brackets ("()") } else if (special == 1) strcpy(SimpleCommand, "ClrHome"); else if (special == 2) { if (!(FileSymPtr = FindFile("amsext", SimpleCommand))) { // the file was not found ST_helpMsg(MSG_ERROR_AMSEXT_NOT_FOUND); return; } // SimpleCommand contains now the foldername, so add.. strcat(SimpleCommand, "\\"); // .. the backslash ("\") strcat(SimpleCommand, "amsext"); // .. the filename strcat(SimpleCommand, "(\"c\")"); // .. the brackets and arguments ("("c")") } else if (special == 3) { sprintf(SimpleCommand, "%s()", name); } else return; // push it to the estack push_parse_text(SimpleCommand); // we cannot use HS_popEStack() in a TSR, because it would pop ALL the estack ! // (even what the AMS has pushed before).(Thank you Kevin Kofler for having // fixed this) // so we are using Samuel Stearley's trick : we copy just what has been added // to the stack in a block of memory. { unsigned short ParsedDataSize = *TopEstackPtr - TopEstackBckp; unsigned short *NewBlockPtr; // + 2 for the size at the beginning of the block if (!(CommandHandle = HeapAllocHigh (ParsedDataSize + 2))) { ST_helpMsg(MSG_ERROR_MEMORY); return; } NewBlockPtr = HeapDeref (CommandHandle); // NG_execute needs this *NewBlockPtr++ = ParsedDataSize; // TopEstackBackup + 1 points to the parsed data memcpy (NewBlockPtr, TopEstackBckp + 1, ParsedDataSize); *TopEstackPtr = TopEstackBckp; // restore the top_estack pointer } // now we can execute the command, and catch all errors may be thrown TRY // NG_execute() can be used because HW220xTSR is installed NG_execute(CommandHandle, 0); ONERR // if an error is thrown, just display the error message ERD_dialog(errCode, FALSE); ENDTRY // free the command-handle HeapFree(CommandHandle); }; #if defined(USE_V200) // ------------------------[ BatteryState ]------------------------- // // Gets the battery state // // ----------------------------------------------------------------- // // input: // // is_hw_2 = indicates a HW2 calculator // // return: // // 7 - full battery state // // 6 - ok battery state // // 5 - medium battery state // // 4 - low battery state (battery symbol appears) // // 3 - very low battery state (battery symbol darkened) // // 2 - starving battery state (battery symbol darkened) // // 1 - almost dead battery state (battery symbol darkened) // // 0 - dead battery state (calc resetted) // // note: // // Taken from TICT-Explorer source code. // // Don't use this function directly, use GetBatteryState instead // // ----------------------------------------------------------------- // unsigned short BatteryState(unsigned short is_hw_2); asm("BatteryState:\n" " movem.l %d1/%a0-%a1,-(%sp)\n" " tst.w 16(%sp) | jump for HW1\n" " beq _bat_h1_1\n" " lea 0x70001D,%a0\n" " move.b (%a0),%d0\n" " ori.b #9,%d0\n" " move.b %d0,(%a0)\n" "_bat_h1_1: moveq #6,%d1\n" " lea 0x600018,%a1\n" "_bat_loop: bsr.s _bat_ini | this initialization lacked in previous versions\n" " move.w %d1,%d0\n" " lsl.w #7,%d0\n" " move.w %d0,(%a1)\n" " moveq #0x6e,%d0\n" "_bat_test: btst.b #2,-0x18(%a1)\n" " dbeq %d0,_bat_test\n" " beq.s _bat_quit\n" " dbf %d1,_bat_loop\n" "_bat_quit: neg.w %d1\n" " addq.w #6,%d1\n" " tst.w 4(%sp)\n" " beq.s _bat_h1_2 | jump for HW1\n" " and.b #0xF7,(%a0)\n" " bsr.s _bat_ini\n" " andi.b #0xF6,(%a0)\n" "_bat_h1_2: move.w %d1,%d0\n" " movem.l (%sp)+,%d1/%a0-%a1\n" " rts\n" "_bat_ini: move.w #0x380,(%a1)\n" " moveq #52,%d0\n" "_bat_wait: btst.b #2,-0x18(%a1)\n" " dbeq %d0,_bat_wait\n" " rts"); // ----------------------[ GetBatteryState ]------------------------ // // Returns the battery state as a value between 0 and 7 // // ----------------------------------------------------------------- // // local: // // hwpb = hwpb // // rombase = rombase // // level = battery level // // note: // // Taken from TICT-Explorer source code // // // // return value | meaning | icon // // --------------|--------------------------------------|------- // // 0 | "0/6 (~ 2.9V, CALC RESETTED)" | 0/8 // // 1 | "0/6 (~ 3.2V, CHANGE VERY QUICKLY)" | 0/8 // // 2 | "1/6 (~ 3.4V, ALMOST DEAD)" | 1/8 // // 3 | "2/6 (~ 3.7V, VERY LOW)" | 2/8 // // 4 | "3/6 (~ 4V, LOW)" | 3/8 // // 5 | "4/6 (~ 4.3V, MEDIUM)" | 5/8 // // 6 | "5/6 (~ 4.5V, OK)" | 7/8 // // 7 | "6/6 (> 4.6V, FULL)" | 8/8 // // ----------------------------------------------------------------- // unsigned short GetBatteryState(void) { unsigned long hwpb, *rombase; unsigned short level; rombase = (unsigned long *)((*(unsigned long *)0xC8) & 0x600000); hwpb = rombase[65]; OSSetSR(0x700); level = BatteryState(((unsigned short)(hwpb - (unsigned long)rombase < 0x10000 && *(unsigned short *)hwpb > 22 ? *(unsigned long *)(hwpb + 22) : 1) - 1L)); OSSetSR(0); return level; }; #endif // -------------------[ default_all_settings ]---------------------- // // Sets all settings to their default values // // ----------------------------------------------------------------- // // note: // // This fuction assumes that points to a valid cofig // // structure. // // The settings may not directly take influence. You need to // // call first. // // ----------------------------------------------------------------- // void default_all_settings(OPTIONS* pref_) { /*pref_->scrolldelay = 18; pref_->keydelay = 50;*/ pref_->cursor_rate = 6; pref_->apd = 120*20; pref_->auto_add_brackets = 1; pref_->clock_type = 0; pref_->battery_type = 1; pref_->keyboard_layout = 0; pref_->autostart_prgm[0] = 0; pref_->_2nd_MEM[0] = 0; strcpy(pref._2nd_CHAR, "main\\easychar"); pref_->off_by_on = 1; }