/******************************************************************************
* *
* 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;
}