/* Miscellaniaous sub-routines for Sim. */ #include "sim.h" #include "misc.h" #include "common.h" #include "parser.h" #include "ttunpack.h" __attribute__ ((noreturn)) static inline void PrintUsage (void) { fatal (CCST ("use: sim(\"prgm\"[,\"config\"][,\"options\"])", "use: sim(\"prgm\"[,\"config\"][,\"option1[|option2]\"])")); } /* A backup of top_estack when Sim is called. */ static ESI Top_estackBackup = NULL; /* Restore top_estack. * This function is registered with atexit(). */ static void RestoreTop_estack (void) { top_estack = Top_estackBackup; } /* Read the arguments passed to the program, and fill the matrix pointed to by REMAP_MATRIX with the data found in the keys script. Return the HSYM structure of the program to emulate. Write the type of keyboard emulation in KBD_EMU_TYPE (ngetchx only, lowlevel only, both, and forced or not). If KbdEmu_NGETCHX is returned, nothing is written to REMAP_MATRIX. Write TRUE to the variable pointed to by EMULATING_THIS_CALC if the user only wants to remap a program for his calc. */ HSYM GetArgsAndKbdMat (T_REMAP_MATRIX *remap_matrix, KbdEmuType *kbd_emu_type, BOOL *emulating_this_calc) { unsigned short argn; ESI argptr; const char *prgm_name, *key_cfg; char str_buffer[sizeof ("Sim: \'folder99\\varname9\' not found...")]; HSYM prgm_hsym; BOOL forced_kbd_emu; unsigned short config_len; #ifdef USE_PEDROM unsigned long prgm_name_len; #endif Top_estackBackup = argptr = top_estack; xatexit ((atexit_t)RestoreTop_estack); argn = RemainingArgCnt (argptr); if (!argn || GetArgType (argptr) != STR_TAG) PrintUsage (); /* NOTREACHED */ prgm_name = (char*)GetStrnArg (argptr); *emulating_this_calc = FALSE; forced_kbd_emu = FALSE; if (argn >= 2) { if (GetArgType (argptr) != STR_TAG) PrintUsage (); /* NOTREACHED */ key_cfg = (char*)GetStrnArg (argptr); if (argn >= 3 && GetArgType (argptr) == STR_TAG) { /* Reads the options. */ #define MAX_OPTIONS_NUM 2 ESI estack_backup; char *strptr, *tempptr; BOOL first_loop = TRUE; estack_backup = argptr; strptr = strtok((char*)GetStrnArg (argptr), "|"); /* As long as there are options to read. */ for (first_loop = TRUE; strptr; first_loop = FALSE, strptr = strtok (NULL, "|")) { if (!first_loop) { tempptr = strptr; /* More than one '|' may have been eaten by strtok(). */ while (*tempptr) tempptr--; /* Remove the '\0' written by strtok(). */ *tempptr = '|'; } if (!strcmp (strptr, "thiscalc")) *emulating_this_calc = TRUE; else if (!strcmp (strptr, "force")) forced_kbd_emu = TRUE; else if (first_loop) { /* The first option is not an option : the string is a parameter for the program. We have read a parameter we should not have read. */ argptr = estack_backup; break; } else /* There was a correct option, but this one incorrect. */ PrintUsage(); } } /* if (argn >= 3 ... */ } else /* argptr == 1 */ key_cfg = "default"; #ifndef USE_PEDROM if (TokenizeName (prgm_name, str_buffer)) PrintUsage (); /* invalid variable name */ prgm_hsym = SymFind (str_buffer + MAX_SYM_LEN); #else /* USE_PEDROM */ prgm_name_len = strlen (prgm_name); if (prgm_name_len >= MAX_SYM_LEN) PrintUsage (); /* invalid variable name */ str_buffer[0] = '\0'; strcpy (&str_buffer[1], prgm_name); str_buffer[prgm_name_len + sizeof ((char)'\0')] = '\0'; prgm_hsym = SymFind (&str_buffer[prgm_name_len + sizeof ((char)'\0')]); #endif /* USE_PEDROM */ if (!*(long*)&prgm_hsym) { sprintf (str_buffer, "Sim: '%s' not found...", prgm_name); fatal (str_buffer); /* NOTREACHED */ } /* New top_estack for the program emulated, wich will be able * to read its command line parameters. Must be *after* * TokenizeName() which calls TokenizeSymName() (wich uses the * estack !) */ top_estack = argptr; config_len = strlen (key_cfg); if (config_len > MAX_CFG_NAME_LEN) fatal ("Sim: configuration name too long."); /* NOTREACHED */ if (!config_len) fatal ("Sim: invalid key config."); /* NOTREACHED */ if (key_cfg[0] == '_' && config_len >= 2) { /* No ngetchx() emulation. */ *kbd_emu_type = forced_kbd_emu ? KbdEmu_FORCED_LOWLEVEL : KbdEmu_LOWLEVEL; key_cfg++; /* jump the '_' */ ParseKeysScript (remap_matrix, key_cfg, *emulating_this_calc); } else if (!cmpstri (key_cfg, "none")) *kbd_emu_type = KbdEmu_NGETCHX; /* no lowlevel emulation */ else { /* Emulate both. */ *kbd_emu_type = (forced_kbd_emu ? KbdEmu_FORCED_LOWLEVEL : KbdEmu_LOWLEVEL) | KbdEmu_NGETCHX; ParseKeysScript (remap_matrix, key_cfg, *emulating_this_calc); } return prgm_hsym; } /* Copy the program of HSYM structure PRGM_HSYM in a block of memory (decompress it if it is a PPG file). Return a pointer to it. The block can be free with xfree(). */ void *CopyPrgmToRAM (HSYM prgm_hsym) { unsigned char *prgm, *prgm_cp; unsigned long plen; static const unsigned char PPG_SIGN[] = {0, 'p', 'p', 'g', 0, 0xF8}; prgm = HeapDeref (((SYM_ENTRY*)DerefSym (prgm_hsym))->handle); plen = (unsigned long)(*(unsigned short*)prgm + 2); if (*(prgm + plen - 1) != (unsigned char)ASM_TAG) { if (memcmp (prgm + plen - sizeof PPG_SIGN, PPG_SIGN, sizeof PPG_SIGN) || !(ttunpack_valid (prgm + 2))) fatal ("Sim: neither ASM nor PPG file..."); /* NOTREACHED */ plen = ttunpack_size (prgm + 2); prgm_cp = xmalloc (plen); xST_helpMsg ("decompressing..."); if (ttunpack_decompress (prgm + 2, prgm_cp)) fatal ("Sim: Invalid PPG file."); /* NOTREACHED */ ST_eraseHelp(); } else { prgm_cp = xmalloc (plen); memcpy (prgm_cp, prgm, plen); } return prgm_cp; } /* EX_patch and run the program whose code is pointed by PRGM (including the first two bytes containing the size). */ void RunPrgm (void *prgm) { (char*)prgm += 0x40000UL; EX_patch ((char*)prgm + 2, *(unsigned short*)prgm + (char*)prgm + 1); ASM_fastcall (prgm + 2); }