| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686 |
- /*
- * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
- *
- * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * ----------------------------------------------------------------------------
- * Interactive Shell implementation
- * ----------------------------------------------------------------------------
- */
- #include "jsutils.h"
- #include "jsinteractive.h"
- #include "jshardware.h"
- #include "jswrapper.h"
- #include "jswrap_json.h"
- #include "jswrap_io.h"
- #ifdef USE_NET
- #include "httpserver.h"
- #endif
- #ifdef USE_LCD_FSMC
- #include "graphics.h"
- #include "lcd_fsmc.h"
- #endif
- #ifdef ARM
- #define CHAR_DELETE_SEND 0x08
- #else
- #define CHAR_DELETE_SEND '\b'
- #endif
- // ----------------------------------------------------------------------------
- typedef struct TimerState {
- JsSysTime time;
- JsSysTime interval;
- bool recurring;
- JsVarRef callback; // a calback, or 0
- } TimerState;
- typedef enum {
- IS_NONE,
- IS_HAD_R,
- IS_HAD_27,
- IS_HAD_27_79,
- IS_HAD_27_91,
- IS_HAD_27_91_49,
- IS_HAD_27_91_51,
- IS_HAD_27_91_52,
- IS_HAD_27_91_53,
- IS_HAD_27_91_54,
- } InputState;
- TODOFlags todo = TODO_NOTHING;
- JsVar *events = 0; // Array of events to execute
- JsVarRef timerArray = 0; // Linked List of timers to check and run
- JsVarRef watchArray = 0; // Linked List of input watches to check and run
- // ----------------------------------------------------------------------------
- IOEventFlags consoleDevice = DEFAULT_CONSOLE_DEVICE; ///< The console device for user interaction
- Pin pinBusyIndicator = DEFAULT_BUSY_PIN_INDICATOR;
- Pin pinSleepIndicator = DEFAULT_SLEEP_PIN_INDICATOR;
- bool echo; ///< do we provide any user feedback?
- bool allowDeepSleep;
- // ----------------------------------------------------------------------------
- JsParse p; ///< The parser we're using for interactiveness
- JsVar *inputLine = 0; ///< The current input line
- bool inputLineRemoved = false;
- int inputCursorPos = 0; ///< The position of the cursor in the input line
- InputState inputState = 0; ///< state for dealing with cursor keys
- bool hasUsedHistory = false; ///< Used to speed up - if we were cycling through history and then edit, we need to copy the string
- unsigned char loopsIdling; ///< How many times around the loop have we been entirely idle?
- bool interruptedDuringEvent; ///< Were we interrupted while executing an event? If so may want to clear timers
- // ----------------------------------------------------------------------------
- IOEventFlags jsiGetDeviceFromClass(JsVar *class) {
- // Built-in classes have their object data set to the device name
- return jshFromDeviceString(class->varData.str);
- }
- JsVar *jsiGetClassNameFromDevice(IOEventFlags device) {
- const char *deviceName = jshGetDeviceString(device);
- return jsvFindChildFromString(p.root, deviceName, false);
- }
- static inline bool jsiShowInputLine() {
- return echo && !inputLineRemoved;
- }
- /// Change the console to a new location
- void jsiSetConsoleDevice(IOEventFlags device) {
- if (device == consoleDevice) return;
- if (!jshIsDeviceInitialised(device)) {
- JshUSARTInfo inf;
- jshUSARTInitInfo(&inf);
- jshUSARTSetup(device, &inf);
- }
- jsiConsoleRemoveInputLine();
- if (echo) { // intentionally not using jsiShowInputLine()
- jsiConsolePrint("Console Moved to ");
- jsiConsolePrint(jshGetDeviceString(device));
- jsiConsolePrint("\n");
- }
- IOEventFlags oldDevice = consoleDevice;
- consoleDevice = device;
- if (echo) { // intentionally not using jsiShowInputLine()
- jsiConsolePrint("Console Moved from ");
- jsiConsolePrint(jshGetDeviceString(oldDevice));
- jsiConsolePrint("\n");
- }
- }
- /// Get the device that the console is currently on
- IOEventFlags jsiGetConsoleDevice() {
- return consoleDevice;
- }
- void jsiConsolePrintChar(char data) {
- jshTransmit(consoleDevice, (unsigned char)data);
- }
- void jsiConsolePrint(const char *str) {
- while (*str) {
- if (*str == '\n') jsiConsolePrintChar('\r');
- jsiConsolePrintChar(*(str++));
- }
- }
- void jsiConsolePrintf(const char *fmt, ...) {
- va_list argp;
- va_start(argp, fmt);
- vcbprintf((vcbprintf_callback)jsiConsolePrint,0, fmt, argp);
- va_end(argp);
- }
- void jsiConsolePrintInt(JsVarInt d) {
- char buf[32];
- itoa(d, buf, 10);
- jsiConsolePrint(buf);
- }
- /// Print the contents of a string var from a character position until end of line (adding an extra ' ' to delete a character if there was one)
- void jsiConsolePrintStringVarUntilEOL(JsVar *v, int fromCharacter, bool andBackup) {
- int chars = 0;
- JsvStringIterator it;
- jsvStringIteratorNew(&it, v, fromCharacter);
- while (jsvStringIteratorHasChar(&it)) {
- char ch = jsvStringIteratorGetChar(&it);
- if (ch == '\n') break;
- jsiConsolePrintChar(ch);
- chars++;
- jsvStringIteratorNext(&it);
- }
- jsvStringIteratorFree(&it);
- if (andBackup) {
- jsiConsolePrintChar(' ');chars++;
- while (chars--) jsiConsolePrintChar(0x08); //delete
- }
- }
- /** Print the contents of a string var - directly - starting from the given character, and
- * using newLineCh to prefix new lines (if it is not 0). */
- void jsiConsolePrintStringVarWithNewLineChar(JsVar *v, int fromCharacter, char newLineCh) {
- JsvStringIterator it;
- jsvStringIteratorNew(&it, v, fromCharacter);
- while (jsvStringIteratorHasChar(&it)) {
- char ch = jsvStringIteratorGetChar(&it);
- if (ch == '\n') jsiConsolePrintChar('\r');
- jsiConsolePrintChar(ch);
- if (ch == '\n' && newLineCh) jsiConsolePrintChar(newLineCh);
- jsvStringIteratorNext(&it);
- }
- jsvStringIteratorFree(&it);
- }
- /// Print the contents of a string var - directly
- void jsiConsolePrintStringVar(JsVar *v) {
- jsiConsolePrintStringVarWithNewLineChar(v,0,0);
- }
- /** Assuming that we are at the end of the string, this backs up
- * and deletes it */
- void jsiConsoleEraseStringVarBackwards(JsVar *v) {
- assert(jsvHasCharacterData(v));
- int line, lines = jsvGetLinesInString(v);
- for (line=lines;line>0;line--) {
- int i,chars = jsvGetCharsOnLine(v, line);
- if (line==lines) {
- for (i=0;i<chars;i++) jsiConsolePrintChar(0x08); // move cursor back
- }
- for (i=0;i<chars;i++) jsiConsolePrintChar(' '); // move cursor forwards and wipe out
- for (i=0;i<chars;i++) jsiConsolePrintChar(0x08); // move cursor back
- if (line>1) {
- // clear the character before - this would have had a colon
- jsiConsolePrintChar(0x08);
- jsiConsolePrintChar(' ');
- // move cursor up
- jsiConsolePrintChar(27);
- jsiConsolePrintChar(91);
- jsiConsolePrintChar(65);
- }
- }
- }
- /** Assuming that we are at fromCharacter position in the string var,
- * erase everything that comes AFTER and return the cursor to 'fromCharacter'
- * On newlines, if erasePrevCharacter, we remove the character before too. */
- void jsiConsoleEraseStringVarFrom(JsVar *v, int fromCharacter, bool erasePrevCharacter) {
- assert(jsvHasCharacterData(v));
- int cursorLine, cursorCol;
- jsvGetLineAndCol(v, fromCharacter, &cursorLine, &cursorCol);
- // delete contents of current line
- int i, chars = jsvGetCharsOnLine(v, cursorLine);
- for (i=cursorCol;i<=chars;i++) jsiConsolePrintChar(' ');
- for (i=0;i<chars;i++) jsiConsolePrintChar(0x08); // move cursor back
- int line, lines = jsvGetLinesInString(v);
- for (line=cursorLine+1;line<=lines;line++) {
- jsiConsolePrintChar(27);
- jsiConsolePrintChar(91);
- jsiConsolePrintChar(66); // move down
- chars = jsvGetCharsOnLine(v, line);
- for (i=0;i<chars;i++) jsiConsolePrintChar(' '); // move cursor forwards and wipe out
- for (i=0;i<chars;i++) jsiConsolePrintChar(0x08); // move cursor back
- if (erasePrevCharacter) {
- jsiConsolePrintChar(0x08); // move cursor back
- jsiConsolePrintChar(' ');
- }
- }
- // move the cursor back up
- for (line=cursorLine+1;line<=lines;line++) {
- jsiConsolePrintChar(27);
- jsiConsolePrintChar(91);
- jsiConsolePrintChar(65);
- }
- // move the cursor forwards
- for (i=1;i<cursorCol;i++) {
- jsiConsolePrintChar(27);
- jsiConsolePrintChar(91);
- jsiConsolePrintChar(67);
- }
- }
- void jsiMoveCursor(int oldX, int oldY, int newX, int newY) {
- // see http://www.termsys.demon.co.uk/vtansi.htm - we could do this better
- // move cursor
- while (oldX < newX) {
- jsiConsolePrintChar(27);
- jsiConsolePrintChar(91);
- jsiConsolePrintChar(67);
- oldX++;
- }
- while (oldX > newX) {
- jsiConsolePrintChar(27);
- jsiConsolePrintChar(91);
- jsiConsolePrintChar(68);
- oldX--;
- }
- while (oldY < newY) {
- jsiConsolePrintChar(27);
- jsiConsolePrintChar(91);
- jsiConsolePrintChar(66);
- oldY++;
- }
- while (oldY > newY) {
- jsiConsolePrintChar(27);
- jsiConsolePrintChar(91);
- jsiConsolePrintChar(65);
- oldY--;
- }
- }
- void jsiMoveCursorChar(JsVar *v, int fromCharacter, int toCharacter) {
- if (fromCharacter==toCharacter) return;
- int oldX, oldY;
- jsvGetLineAndCol(v, fromCharacter, &oldY, &oldX);
- int newX, newY;
- jsvGetLineAndCol(v, toCharacter, &newY, &newX);
- jsiMoveCursor(oldX, oldY, newX, newY);
- }
- /// If the input line was shown in the console, remove it
- void jsiConsoleRemoveInputLine() {
- if (!inputLineRemoved) {
- inputLineRemoved = true;
- if (echo && inputLine) { // intentionally not using jsiShowInputLine()
- jsiMoveCursorChar(inputLine, inputCursorPos, 0);
- jsiConsoleEraseStringVarFrom(inputLine, 0, true);
- jsiConsolePrintChar(0x08); // go back to start of line
- }
- }
- }
- /// If the input line has been removed, return it
- void jsiReturnInputLine() {
- if (inputLineRemoved) {
- inputLineRemoved = false;
- if (echo) { // intentionally not using jsiShowInputLine()
- jsiConsolePrintChar('\r'); // carriage return
- jsiConsolePrintChar('>');
- jsiConsolePrintStringVarWithNewLineChar(inputLine, 0, ':');
- jsiMoveCursorChar(inputLine, (int)jsvGetStringLength(inputLine), inputCursorPos);
- }
- }
- }
- void jsiConsolePrintPosition(struct JsLex *lex, int tokenPos) {
- int line,col;
- jsvGetLineAndCol(lex->sourceVar, tokenPos, &line, &col);
- jsiConsolePrintf("line %d col %d\n",line,col);
- }
- void jsiConsolePrintTokenLineMarker(struct JsLex *lex, int tokenPos) {
- int line = 1,col = 1;
- jsvGetLineAndCol(lex->sourceVar, tokenPos, &line, &col);
- int startOfLine = jsvGetIndexFromLineAndCol(lex->sourceVar, line, 1);
- jsiConsolePrintStringVarUntilEOL(lex->sourceVar, startOfLine, false);
- jsiConsolePrint("\n");
- while (col-- > 1) jsiConsolePrintChar(' ');
- jsiConsolePrintChar('^');
- jsiConsolePrint("\n");
- }
- /// Print the contents of a string var to a device - directly
- void jsiTransmitStringVar(IOEventFlags device, JsVar *v) {
- JsvStringIterator it;
- jsvStringIteratorNew(&it, v, 0);
- while (jsvStringIteratorHasChar(&it)) {
- char ch = jsvStringIteratorGetChar(&it);
- jshTransmit(device, (unsigned char)ch);
- jsvStringIteratorNext(&it);
- }
- jsvStringIteratorFree(&it);
- }
- void jsiSetBusy(JsiBusyDevice device, bool isBusy) {
- static JsiBusyDevice business = 0;
- if (isBusy)
- business |= device;
- else
- business &= (JsiBusyDevice)~device;
- if (pinBusyIndicator != PIN_UNDEFINED)
- jshPinOutput(pinBusyIndicator, business!=0);
- }
- void jsiSetSleep(bool isSleep) {
- if (pinSleepIndicator != PIN_UNDEFINED)
- jshPinOutput(pinSleepIndicator, isSleep);
- }
- static JsVarRef _jsiInitNamedArray(const char *name) {
- JsVar *arrayName = jsvFindChildFromString(p.root, name, true);
- if (!arrayName) return 0; // out of memory
- if (!arrayName->firstChild) {
- JsVar *array = jsvNewWithFlags(JSV_ARRAY);
- if (!array) { // out of memory
- jsvUnLock(arrayName);
- return 0;
- }
- arrayName->firstChild = jsvGetRef(jsvRef(array));
- jsvUnLock(array);
- }
- JsVarRef arrayRef = jsvRefRef(arrayName->firstChild);
- jsvUnLock(arrayName);
- return arrayRef;
- }
- // Used when recovering after being flashed
- // 'claim' anything we are using
- void jsiSoftInit() {
- #ifdef USE_NET
- httpInit();
- #endif
- #ifdef USE_LCD_FSMC
- JsVar *parent = jspNewObject(jsiGetParser(), "LCD", "Graphics");
- if (parent) {
- JsVar *parentObj = jsvSkipName(parent);
- JsGraphics gfx;
- graphicsStructInit(&gfx);
- gfx.data.type = JSGRAPHICSTYPE_FSMC;
- gfx.graphicsVar = parentObj;
- gfx.data.width = 320;
- gfx.data.height = 240;
- gfx.data.bpp = 16;
- lcdInit_FSMC(&gfx);
- lcdSetCallbacks_FSMC(&gfx);
- graphicsSplash(&gfx);
- graphicsSetVar(&gfx);
- jsvUnLock(parentObj);
- jsvUnLock(parent);
- }
- #endif
- events = jsvNewWithFlags(JSV_ARRAY);
- inputLine = jsvNewFromEmptyString();
- inputCursorPos = 0;
- allowDeepSleep = false;
- // Load timer/watch arrays
- timerArray = _jsiInitNamedArray(JSI_TIMERS_NAME);
- watchArray = _jsiInitNamedArray(JSI_WATCHES_NAME);
- // Now run initialisation code
- JsVar *initName = jsvFindChildFromString(p.root, JSI_INIT_CODE_NAME, false);
- if (initName && initName->firstChild) {
- //jsiConsolePrint("Running initialisation code...\n");
- JsVar *initCode = jsvLock(initName->firstChild);
- jsvUnLock(jspEvaluateVar(&p, initCode, 0));
- jsvUnLock(initCode);
- jsvRemoveChild(p.root, initName);
- }
- jsvUnLock(initName);
- // Check any existing watches and set up interrupts for them
- if (watchArray) {
- JsVar *watchArrayPtr = jsvLock(watchArray);
- JsVarRef watch = watchArrayPtr->firstChild;
- while (watch) {
- JsVar *watchNamePtr = jsvLock(watch);
- JsVar *watchPin = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
- jshPinWatch(jshGetPinFromVar(watchPin), true);
- jsvUnLock(watchPin);
- watch = watchNamePtr->nextSibling;
- jsvUnLock(watchNamePtr);
- }
- jsvUnLock(watchArrayPtr);
- }
- // Check any existing timers and try and set time correctly
- if (timerArray) {
- JsSysTime currentTime = jshGetSystemTime();
- JsVar *timerArrayPtr = jsvLock(timerArray);
- JsVarRef timer = timerArrayPtr->firstChild;
- while (timer) {
- JsVar *timerNamePtr = jsvLock(timer);
- JsVar *timerTime = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "time", false));
- JsVarFloat interval = jsvGetFloatAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "interval", false)));
- jsvSetInteger(timerTime, currentTime + jshGetTimeFromMilliseconds(interval));
- jsvUnLock(timerTime);
- timer = timerNamePtr->nextSibling;
- jsvUnLock(timerNamePtr);
- }
- jsvUnLock(timerArrayPtr);
- }
- // And look for onInit function
- JsVar *onInit = jsvFindChildFromString(p.root, JSI_ONINIT_NAME, false);
- if (onInit && onInit->firstChild) {
- if (echo) jsiConsolePrint("Running onInit()...\n");
- JsVar *func = jsvSkipName(onInit);
- if (jsvIsFunction(func))
- jspExecuteFunction(&p, func, 0, 0, (JsVar**)0);
- else if (jsvIsString(func))
- jsvUnLock(jspEvaluateVar(&p, func, 0));
- else
- jsError("onInit is not a Function or a String");
- }
- jsvUnLock(onInit);
- }
- /** Append the code required to initialise a serial port to this string */
- void jsiAppendSerialInitialisation(JsVar *str, const char *serialName, bool addCallbacks) {
- JsVar *serialVar = jsvObjectGetChild(p.root, serialName, 0);
- if (serialVar) {
- if (addCallbacks) {
- JsVar *onData = jsvSkipOneNameAndUnLock(jsvFindChildFromString(serialVar, USART_CALLBACK_NAME, false));
- if (onData) {
- JsVar *onDataStr = jsvAsString(onData, true/*unlock*/);
- jsvAppendString(str, serialName);
- jsvAppendString(str, ".onData(");
- jsvAppendStringVarComplete(str, onDataStr);
- jsvAppendString(str, ");\n");
- jsvUnLock(onDataStr);
- }
- }
- JsVar *baud = jsvObjectGetChild(serialVar, USART_BAUDRATE_NAME, 0);
- JsVar *options = jsvObjectGetChild(serialVar, DEVICE_OPTIONS_NAME, 0);
- if (baud || options) {
- JsVarInt baudrate = jsvGetInteger(baud);
- if (baudrate <= 0) baudrate = DEFAULT_BAUD_RATE;
- jsvAppendPrintf(str, "%s.setup(%d", serialName, baudrate);
- if (jsvIsObject(options)) {
- jsvAppendString(str, ", ");
- jsfGetJSON(options, str);
- }
- jsvAppendString(str, ");\n");
- }
- jsvUnLock(baud);
- jsvUnLock(options);
- jsvUnLock(serialVar);
- }
- }
- /** Append the code required to initialise a SPI port to this string */
- void jsiAppendDeviceInitialisation(JsVar *str, const char *deviceName) {
- JsVar *deviceVar = jsvObjectGetChild(p.root, deviceName, 0);
- if (deviceVar) {
- JsVar *options = jsvObjectGetChild(deviceVar, DEVICE_OPTIONS_NAME, 0);
- if (options) {
- jsvAppendString(str, deviceName);
- jsvAppendString(str, ".setup(");
- if (jsvIsObject(options)) {
- jsfGetJSON(options, str);
- }
- jsvAppendString(str, ");\n");
- }
- jsvUnLock(options);
- jsvUnLock(deviceVar);
- }
- }
- /** Append all the code required to initialise hardware to this string */
- void jsiAppendHardwareInitialisation(JsVar *str, bool addCallbacks) {
- if (!echo) jsvAppendString(str, "echo(0);");
- if (pinBusyIndicator != DEFAULT_BUSY_PIN_INDICATOR) {
- jsvAppendPrintf(str, "setBusyIndicator(%p);\n", pinBusyIndicator);
- }
- if (pinSleepIndicator != DEFAULT_BUSY_PIN_INDICATOR) {
- jsvAppendPrintf(str, "setSleepIndicator(%p);\n", pinSleepIndicator);
- }
- if (allowDeepSleep) {
- jsvAppendPrintf(str, "setDeepSleep(1);\n");
- }
- jsiAppendSerialInitialisation(str, "USB", addCallbacks);
- int i;
- for (i=0;i<USARTS;i++)
- jsiAppendSerialInitialisation(str, jshGetDeviceString(EV_SERIAL1+i), addCallbacks);
- for (i=0;i<SPIS;i++)
- jsiAppendDeviceInitialisation(str, jshGetDeviceString(EV_SPI1+i));
- for (i=0;i<I2CS;i++)
- jsiAppendDeviceInitialisation(str, jshGetDeviceString(EV_I2C1+i));
- // pins
- Pin pin;
- for (pin=0;jshIsPinValid(pin) && pin<255;pin++) {
- if (IS_PIN_USED_INTERNALLY(pin)) continue;
- JshPinState state = jshPinGetState(pin);
- JshPinState statem = state&JSHPINSTATE_MASK;
- if (statem == JSHPINSTATE_GPIO_OUT) {
- bool isOn = (state&JSHPINSTATE_PIN_IS_ON)!=0;
- if (!isOn && IS_PIN_A_LED(pin)) continue;
- jsvAppendPrintf(str, "digitalWrite(%p,%d);\n",pin,isOn?1:0);
- } else if (/*statem == JSHPINSTATE_GPIO_IN ||*/statem == JSHPINSTATE_GPIO_IN_PULLUP || statem == JSHPINSTATE_GPIO_IN_PULLDOWN) {
- // don't bother with normal inputs, as they come up in this state (ish) anyway
- const char *s = "";
- if (statem == JSHPINSTATE_GPIO_IN_PULLUP) s="_pullup";
- if (statem == JSHPINSTATE_GPIO_IN_PULLDOWN) s="_pulldown";
- jsvAppendPrintf(str, "pinMode(%p,input%s);\n",pin,s);
- }
- }
- }
- // Used when shutting down before flashing
- // 'release' anything we are using, but ensure that it doesn't get freed
- void jsiSoftKill() {
- jsvUnLock(inputLine);
- inputLine=0;
- inputCursorPos = 0;
- // Unref Watches/etc
- if (events) {
- jsvUnLock(events);
- events=0;
- }
- if (timerArray) {
- jsvUnRefRef(timerArray);
- timerArray=0;
- }
- if (watchArray) {
- // Check any existing watches and disable interrupts for them
- JsVar *watchArrayPtr = jsvLock(watchArray);
- JsVarRef watch = watchArrayPtr->firstChild;
- while (watch) {
- JsVar *watchNamePtr = jsvLock(watch);
- JsVar *watchPin = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
- jshPinWatch(jshGetPinFromVar(watchPin), false);
- jsvUnLock(watchPin);
- watch = watchNamePtr->nextSibling;
- jsvUnLock(watchNamePtr);
- }
- jsvUnLock(watchArrayPtr);
- jsvUnRefRef(watchArray);
- watchArray=0;
- }
- // Save initialisation information
- JsVar *initName = jsvFindChildFromString(p.root, JSI_INIT_CODE_NAME, true);
- if (initName->firstChild) {
- jsvUnRefRef(initName->firstChild);
- initName->firstChild = 0;
- }
- JsVar *initCode = jsvNewFromEmptyString();
- if (initCode) { // out of memory
- initName->firstChild = jsvGetRef(jsvRef(initCode));
- jsiAppendHardwareInitialisation(initCode, false);
- jsvUnLock(initCode);
- }
- jsvUnLock(initName);
- #ifdef USE_NET
- httpKill();
- #endif
- }
- void jsiInit(bool autoLoad) {
- jsvInit();
- jspInit(&p);
- #ifdef USE_LCD
- lcdInit_Main(&LCD);
- #ifdef USE_LCD_ILI9325
- lcdInit_FSMC_ILI9325(&LCD, LCD_WIDTH, LCD_HEIGHT);
- #endif
- #ifdef USE_LCD_SDL
- lcdInit_SDL(&LCD, LCD_WIDTH, LCD_HEIGHT);
- #endif
- lcdClear(0);
- lcdSplash();
- #endif
- /*for (i=0;i<IOPINS;i++)
- ioPinState[i].callbacks = 0;*/
- // Set state
- interruptedDuringEvent = false;
- // Set defaults
- echo = true;
- consoleDevice = DEFAULT_CONSOLE_DEVICE;
- pinBusyIndicator = DEFAULT_BUSY_PIN_INDICATOR;
- if (jshIsUSBSERIALConnected())
- consoleDevice = EV_USBSERIAL;
- /* If flash contains any code, then we should
- Try and load from it... */
- if (autoLoad && jshFlashContainsCode()) {
- jspSoftKill(&p);
- jsvSoftKill();
- jshLoadFromFlash();
- jsvSoftInit();
- jspSoftInit(&p);
- }
- //jsvTrace(jsvGetRef(jsiGetParser()->root), 0)
- // Softinit may run initialisation code that will overwrite defaults
- jsiSoftInit();
- if (echo) { // intentionally not using jsiShowInputLine()
- #ifndef LINUX
- // set up terminal to avoid word wrap
- jsiConsolePrint("\e[?7l");
- #endif
- // rectangles @ http://www.network-science.de/ascii/
- jsiConsolePrint("\n"
- " _____ _ \n"
- " | __|___ ___ ___ _ _|_|___ ___ \n"
- " | __|_ -| . | _| | | | | . |\n"
- " |_____|___| _|_| |___|_|_|_|___|");
- jsiConsolePrint("\n"
- " |_| http://www.espruino.com\n"
- " "JS_VERSION" Copyright 2013 Gordon Williams\n"
- "-------------------------------------------\n"
- " KickStarter Version\n"
- "-------------------------------------------\n");
- jsiConsolePrint("\n>");
- }
- }
- void jsiKill() {
- jsiSoftKill();
- jspKill(&p);
- jsvKill();
- }
- int jsiCountBracketsInInput() {
- int brackets = 0;
- JsLex lex;
- jslInit(&lex, inputLine);
- while (lex.tk!=LEX_EOF && lex.tk!=LEX_UNFINISHED_COMMENT) {
- if (lex.tk=='{' || lex.tk=='[' || lex.tk=='(') brackets++;
- if (lex.tk=='}' || lex.tk==']' || lex.tk==')') brackets--;
- if (brackets<0) break; // closing bracket before opening!
- jslGetNextToken(&lex);
- }
- if (lex.tk==LEX_UNFINISHED_COMMENT)
- brackets=1000; // if there's an unfinished comment, we're in the middle of something
- jslKill(&lex);
- return brackets;
- }
- /// Tries to get rid of some memory (by clearing command history). Returns true if it got rid of something, false if it didn't.
- bool jsiFreeMoreMemory() {
- JsVar *history = jsvObjectGetChild(p.root, JSI_HISTORY_NAME, 0);
- if (!history) return 0;
- JsVar *item = jsvArrayPopFirst(history);
- bool freed = item!=0;
- jsvUnLock(item);
- jsvUnLock(history);
- // TODO: could also free the array structure?
- return freed;
- }
- // Add a new line to the command history
- void jsiHistoryAddLine(JsVar *newLine) {
- if (!newLine || jsvGetStringLength(newLine)==0) return;
- JsVar *history = jsvFindChildFromString(p.root, JSI_HISTORY_NAME, true);
- if (!history) return; // out of memory
- // ensure we actually have the history array
- if (!history->firstChild) {
- JsVar *arr = jsvNewWithFlags(JSV_ARRAY);
- if (!arr) {// out of memory
- jsvUnLock(history);
- return;
- }
- history->firstChild = jsvGetRef(jsvRef(arr));
- jsvUnLock(arr);
- }
- history = jsvSkipNameAndUnLock(history);
- // if it was already in history, remove it - we'll put it back in front
- JsVar *alreadyInHistory = jsvGetArrayIndexOf(history, newLine, false/*not exact*/);
- if (alreadyInHistory) {
- jsvRemoveChild(history, alreadyInHistory);
- jsvUnLock(alreadyInHistory);
- }
- // put it back in front
- jsvArrayPush(history, newLine);
- jsvUnLock(history);
- }
- JsVar *jsiGetHistoryLine(bool previous /* next if false */) {
- JsVar *history = jsvObjectGetChild(p.root, JSI_HISTORY_NAME, 0);
- JsVar *historyLine = 0;
- if (history) {
- JsVar *idx = jsvGetArrayIndexOf(history, inputLine, true/*exact*/); // get index of current line
- if (idx) {
- if (previous && idx->prevSibling) {
- historyLine = jsvSkipNameAndUnLock(jsvLock(idx->prevSibling));
- } else if (!previous && idx->nextSibling) {
- historyLine = jsvSkipNameAndUnLock(jsvLock(idx->nextSibling));
- }
- jsvUnLock(idx);
- } else {
- if (previous) historyLine = jsvSkipNameAndUnLock(jsvArrayGetLast(history));
- // if next, we weren't using history so couldn't go forwards
- }
-
- jsvUnLock(history);
- }
- return historyLine;
- }
- bool jsiIsInHistory(JsVar *line) {
- JsVar *history = jsvObjectGetChild(p.root, JSI_HISTORY_NAME, 0);
- if (!history) return false;
- JsVar *historyFound = jsvGetArrayIndexOf(history, line, true/*exact*/);
- bool inHistory = historyFound!=0;
- jsvUnLock(historyFound);
- jsvUnLock(history);
- return inHistory;
- }
- void jsiReplaceInputLine(JsVar *newLine) {
- if (jsiShowInputLine()) {
- int oldLen = (int)jsvGetStringLength(inputLine);
- jsiMoveCursorChar(inputLine, inputCursorPos, oldLen); // move cursor to end
- jsiConsoleEraseStringVarBackwards(inputLine);
- jsiConsolePrintStringVarWithNewLineChar(newLine,0,':');
- }
- jsvUnLock(inputLine);
- inputLine = jsvLockAgain(newLine);
- inputCursorPos = (int)jsvGetStringLength(inputLine);
- }
- void jsiChangeToHistory(bool previous) {
- JsVar *nextHistory = jsiGetHistoryLine(previous);
- if (nextHistory) {
- jsiReplaceInputLine(nextHistory);
- jsvUnLock(nextHistory);
- hasUsedHistory = true;
- } else if (!previous) { // if next, but we have something, just clear the line
- if (jsiShowInputLine()) {
- jsiConsoleEraseStringVarBackwards(inputLine);
- }
- jsvUnLock(inputLine);
- inputLine = jsvNewFromEmptyString();
- inputCursorPos = 0;
- }
- }
- void jsiIsAboutToEditInputLine() {
- // we probably plan to do something with the line now - check it wasn't in history
- // and if it was, duplicate it
- if (hasUsedHistory) {
- hasUsedHistory = false;
- if (jsiIsInHistory(inputLine)) {
- JsVar *newLine = jsvCopy(inputLine);
- if (newLine) { // could have been out of memory!
- jsvUnLock(inputLine);
- inputLine = newLine;
- }
- }
- }
- }
- void jsiHandleDelete(bool isBackspace) {
- int l = (int)jsvGetStringLength(inputLine);
- if (isBackspace && inputCursorPos<=0) return; // at beginning of line
- if (!isBackspace && inputCursorPos>=l) return; // at end of line
- // work out if we are deleting a newline
- bool deleteNewline = (isBackspace && jsvGetCharInString(inputLine,inputCursorPos-1)=='\n') ||
- (!isBackspace && jsvGetCharInString(inputLine,inputCursorPos)=='\n');
- // If we mod this to keep the string, use jsiIsAboutToEditInputLine
- if (deleteNewline && jsiShowInputLine()) {
- jsiConsoleEraseStringVarFrom(inputLine, inputCursorPos, true/*before newline*/); // erase all in front
- if (isBackspace) {
- // delete newline char
- jsiConsolePrintChar(0x08);
- jsiConsolePrintChar(' ');
- jsiMoveCursorChar(inputLine, inputCursorPos, inputCursorPos-1); // move cursor back
- }
- }
- JsVar *v = jsvNewFromEmptyString();
- int p = inputCursorPos;
- if (isBackspace) p--;
- if (p>0) jsvAppendStringVar(v, inputLine, 0, p); // add before cursor (delete)
- if (p+1<l) jsvAppendStringVar(v, inputLine, p+1, JSVAPPENDSTRINGVAR_MAXLENGTH); // add the rest
- jsvUnLock(inputLine);
- inputLine=v;
- if (isBackspace)
- inputCursorPos--; // move cursor back
- // update the console
- if (jsiShowInputLine()) {
- if (deleteNewline) {
- // we already removed everything, so just put it back
- jsiConsolePrintStringVarWithNewLineChar(inputLine, inputCursorPos, ':');
- jsiMoveCursorChar(inputLine, (int)jsvGetStringLength(inputLine), inputCursorPos); // move cursor back
- } else {
- // clear the character and move line back
- if (isBackspace) jsiConsolePrintChar(0x08);
- jsiConsolePrintStringVarUntilEOL(inputLine, inputCursorPos, true/*and backup*/);
- }
- }
- }
- void jsiHandleHome() {
- while (inputCursorPos>0 && jsvGetCharInString(inputLine,inputCursorPos-1)!='\n') {
- if (jsiShowInputLine()) jsiConsolePrintChar(0x08);
- inputCursorPos--;
- }
- }
- void jsiHandleEnd() {
- int l = (int)jsvGetStringLength(inputLine);
- while (inputCursorPos<l && jsvGetCharInString(inputLine,inputCursorPos)!='\n') {
- if (jsiShowInputLine())
- jsiConsolePrintChar(jsvGetCharInString(inputLine,inputCursorPos));
- inputCursorPos++;
- }
- }
- /** Page up/down move cursor to beginnint or end */
- void jsiHandlePageUpDown(bool isDown) {
- int x,y;
- jsvGetLineAndCol(inputLine, inputCursorPos, &y, &x);
- if (!isDown) { // up
- inputCursorPos = 0;
- } else { // down
- inputCursorPos = (int)jsvGetStringLength(inputLine);
- }
- int newX=x,newY=y;
- jsvGetLineAndCol(inputLine, inputCursorPos, &newY, &newX);
- jsiMoveCursor(x,y,newX,newY);
- }
- void jsiHandleMoveUpDown(int direction) {
- int x,y, lines=jsvGetLinesInString(inputLine);
- jsvGetLineAndCol(inputLine, inputCursorPos, &y, &x);
- int newX=x,newY=y;
- newY+=direction;
- if (newY<1) newY=1;
- if (newY>lines) newY=lines;
- // work out cursor pos and feed back through - we might not be able to get right to the same place
- // if we move up
- inputCursorPos = jsvGetIndexFromLineAndCol(inputLine, newY, newX);
- jsvGetLineAndCol(inputLine, inputCursorPos, &newY, &newX);
- if (jsiShowInputLine()) {
- jsiMoveCursor(x,y,newX,newY);
- }
- }
- bool jsiAtEndOfInputLine() {
- int i = inputCursorPos, l = (int)jsvGetStringLength(inputLine);
- while (i < l) {
- if (!isWhitespace(jsvGetCharInString(inputLine, i)))
- return false;
- i++;
- }
- return true;
- }
- void jsiHandleChar(char ch) {
- //jsiConsolePrintf("[%d:%d]\n", inputState, ch);
- //
- // special stuff
- // 27 then 91 then 68 - left
- // 27 then 91 then 67 - right
- // 27 then 91 then 65 - up
- // 27 then 91 then 66 - down
- // 27 then 91 then 51 then 126 - backwards delete
- // 27 then 91 then 52 then 126 - numpad end
- // 27 then 91 then 49 then 126 - numpad home
- // 27 then 91 then 53 then 126 - pgup
- // 27 then 91 then 54 then 126 - pgdn
- // 27 then 79 then 70 - home
- // 27 then 79 then 72 - end
- if (ch == 0) {
- inputState = IS_NONE; // ignore 0 - it's scary
- } else if (ch == 27) {
- inputState = IS_HAD_27;
- } else if (inputState==IS_HAD_27) {
- inputState = IS_NONE;
- if (ch == 79)
- inputState = IS_HAD_27_79;
- else if (ch == 91)
- inputState = IS_HAD_27_91;
- } else if (inputState==IS_HAD_27_79) { // Numpad
- inputState = IS_NONE;
- if (ch == 70) jsiHandleEnd();
- else if (ch == 72) jsiHandleHome();
- else if (ch == 111) jsiHandleChar('/');
- else if (ch == 106) jsiHandleChar('*');
- else if (ch == 109) jsiHandleChar('-');
- else if (ch == 107) jsiHandleChar('+');
- else if (ch == 77) jsiHandleChar('\r');
- } else if (inputState==IS_HAD_27_91) {
- inputState = IS_NONE;
- if (ch==68) { // left
- if (inputCursorPos>0 && jsvGetCharInString(inputLine,inputCursorPos-1)!='\n') {
- inputCursorPos--;
- if (jsiShowInputLine()) {
- jsiConsolePrintChar(27);
- jsiConsolePrintChar(91);
- jsiConsolePrintChar(68);
- }
- }
- } else if (ch==67) { // right
- if (inputCursorPos<(int)jsvGetStringLength(inputLine) && jsvGetCharInString(inputLine,inputCursorPos)!='\n') {
- inputCursorPos++;
- if (jsiShowInputLine()) {
- jsiConsolePrintChar(27);
- jsiConsolePrintChar(91);
- jsiConsolePrintChar(67);
- }
- }
- } else if (ch==65) { // up
- int l = (int)jsvGetStringLength(inputLine);
- if ((l==0 || jsiIsInHistory(inputLine)) && inputCursorPos==l)
- jsiChangeToHistory(true); // if at end of line
- else
- jsiHandleMoveUpDown(-1);
- } else if (ch==66) { // down
- int l = (int)jsvGetStringLength(inputLine);
- if ((l==0 || jsiIsInHistory(inputLine)) && inputCursorPos==l)
- jsiChangeToHistory(false); // if at end of line
- else
- jsiHandleMoveUpDown(1);
- } else if (ch==49) {
- inputState=IS_HAD_27_91_49;
- } else if (ch==51) {
- inputState=IS_HAD_27_91_51;
- } else if (ch==52) {
- inputState=IS_HAD_27_91_52;
- } else if (ch==53) {
- inputState=IS_HAD_27_91_53;
- } else if (ch==54) {
- inputState=IS_HAD_27_91_54;
- }
- } else if (inputState==IS_HAD_27_91_49) {
- inputState = IS_NONE;
- if (ch==126) { // Numpad Home
- jsiHandleHome();
- }
- } else if (inputState==IS_HAD_27_91_51) {
- inputState = IS_NONE;
- if (ch==126) { // Numpad (forwards) Delete
- jsiHandleDelete(false/*not backspace*/);
- }
- } else if (inputState==IS_HAD_27_91_52) {
- inputState = IS_NONE;
- if (ch==126) { // Numpad End
- jsiHandleEnd();
- }
- } else if (inputState==IS_HAD_27_91_53) {
- inputState = IS_NONE;
- if (ch==126) { // Page Up
- jsiHandlePageUpDown(0);
- }
- } else if (inputState==IS_HAD_27_91_54) {
- inputState = IS_NONE;
- if (ch==126) { // Page Down
- jsiHandlePageUpDown(1);
- }
- } else {
- inputState = IS_NONE;
- if (ch == 0x08 || ch == 0x7F /*delete*/) {
- jsiHandleDelete(true /*backspace*/);
- } else if (ch == '\n' && inputState == IS_HAD_R) {
- inputState = IS_NONE; // ignore \ r\n - we already handled it all on \r
- } else if (ch == '\r' || ch == '\n') {
- if (jsiAtEndOfInputLine()) { // at EOL so we need to figure out if we can execute or not
- if (ch == '\r') inputState = IS_HAD_R;
- if (jsiCountBracketsInInput()<=0) { // actually execute!
- if (jsiShowInputLine()) {
- jsiConsolePrintChar('\r');
- jsiConsolePrintChar('\n');
- }
- inputLineRemoved = true;
- // Get line to execute, and reset inputLine
- JsVar *lineToExecute = jsvStringTrimRight(inputLine);
- jsvUnLock(inputLine);
- inputLine = jsvNewFromEmptyString();
- inputCursorPos = 0;
- // execute!
- JsVar *v = jspEvaluateVar(&p, lineToExecute, 0);
- // add input line to history
- jsiHistoryAddLine(lineToExecute);
- jsvUnLock(lineToExecute);
- // print result
- if (echo) { // intentionally not using jsiShowInputLine()
- jsiConsolePrintChar('=');
- jsfPrintJSON(v);
- jsiConsolePrint("\n");
- }
- jsvUnLock(v);
- // console will be returned next time around the input loop
- } else {
- // Brackets aren't all closed, so we're going to append a newline
- // without executing
- if (jsiShowInputLine()) jsiConsolePrint("\n:");
- jsiIsAboutToEditInputLine();
- jsvAppendCharacter(inputLine, '\n');
- inputCursorPos++;
- }
- } else { // new line - but not at end of line!
- jsiIsAboutToEditInputLine();
- if (jsiShowInputLine()) jsiConsoleEraseStringVarFrom(inputLine, inputCursorPos, false/*no need to erase the char before*/); // erase all in front
- JsVar *v = jsvNewFromEmptyString();
- if (inputCursorPos>0) jsvAppendStringVar(v, inputLine, 0, inputCursorPos);
- jsvAppendCharacter(v, '\n');
- jsvAppendStringVar(v, inputLine, inputCursorPos, JSVAPPENDSTRINGVAR_MAXLENGTH); // add the rest
- jsvUnLock(inputLine);
- inputLine=v;
- if (jsiShowInputLine()) { // now print the rest
- jsiConsolePrintStringVarWithNewLineChar(inputLine, inputCursorPos, ':');
- jsiMoveCursorChar(inputLine, (int)jsvGetStringLength(inputLine), inputCursorPos+1); // move cursor back
- }
- inputCursorPos++;
- }
- } else if (ch>=32 || ch=='\t') {
- // Add the character to our input line
- jsiIsAboutToEditInputLine();
- int l = (int)jsvGetStringLength(inputLine);
- bool hasTab = ch=='\t';
- if (inputCursorPos>=l) {
- if (hasTab) jsvAppendString(inputLine, " ");
- else jsvAppendCharacter(inputLine, ch);
- } else {
- JsVar *v = jsvNewFromEmptyString();
- if (inputCursorPos>0) jsvAppendStringVar(v, inputLine, 0, inputCursorPos);
- if (hasTab) jsvAppendString(v, " ");
- else jsvAppendCharacter(v, ch);
- jsvAppendStringVar(v, inputLine, inputCursorPos, JSVAPPENDSTRINGVAR_MAXLENGTH); // add the rest
- jsvUnLock(inputLine);
- inputLine=v;
- if (jsiShowInputLine()) jsiConsolePrintStringVarUntilEOL(inputLine, inputCursorPos, true/*and backup*/);
- }
- inputCursorPos += hasTab ? 4 : 1;
- if (jsiShowInputLine()) {
- if (hasTab) jsiConsolePrint(" ");
- else jsiConsolePrintChar(ch);
- }
- }
- }
- }
- void jsiQueueEvents(JsVarRef callbacks, JsVar *arg0, JsVar *arg1) { // array of functions or single function
- if (!callbacks) return;
- JsVar *callbackVar = jsvLock(callbacks);
- // if it is a single callback, just add it
- if (jsvIsFunction(callbackVar) || jsvIsString(callbackVar)) {
- JsVar *event = jsvNewWithFlags(JSV_OBJECT);
- if (event) { // Could be out of memory error!
- jsvUnLock(jsvAddNamedChild(event, callbackVar, "func"));
- if (arg0) jsvUnLock(jsvAddNamedChild(event, arg0, "arg0"));
- if (arg1) jsvUnLock(jsvAddNamedChild(event, arg1, "arg1"));
- jsvArrayPushAndUnLock(events, event);
- }
- jsvUnLock(callbackVar);
- } else {
- assert(jsvIsArray(callbackVar));
- // go through all callbacks
- JsVarRef next = callbackVar->firstChild;
- jsvUnLock(callbackVar);
- while (next) {
- //jsPrint("Queue Event\n");
- JsVar *child = jsvLock(next);
-
- // for each callback...
- JsVar *event = jsvNewWithFlags(JSV_OBJECT);
- if (event) { // Could be out of memory error!
- jsvUnLock(jsvAddNamedChild(event, child, "func"));
- if (arg0) jsvUnLock(jsvAddNamedChild(event, arg0, "arg0"));
- if (arg1) jsvUnLock(jsvAddNamedChild(event, arg1, "arg1"));
- // add event to the events list
- jsvArrayPushAndUnLock(events, event);
- // go to next callback
- }
- next = child->nextSibling;
- jsvUnLock(child);
- }
- }
- }
- void jsiQueueObjectCallbacks(JsVar *object, const char *callbackName, JsVar *arg0, JsVar *arg1) {
- JsVar *callback = jsvObjectGetChild(object, callbackName, 0);
- if (!callback) return;
- jsiQueueEvents(jsvGetRef(callback), arg0, arg1);
- jsvUnLock(callback);
- }
- void jsiExecuteEvents() {
- bool hasEvents = !jsvArrayIsEmpty(events);
- bool wasInterrupted = jspIsInterrupted();
- if (hasEvents) jsiSetBusy(BUSY_INTERACTIVE, true);
- while (!jsvArrayIsEmpty(events)) {
- JsVar *event = jsvSkipNameAndUnLock(jsvArrayPopFirst(events));
- // Get function to execute
- JsVar *func = jsvObjectGetChild(event, "func", 0);
- JsVar *args[2];
- args[0] = jsvObjectGetChild(event, "arg0", 0);
- args[1] = jsvObjectGetChild(event, "arg1", 0);
- // free
- jsvUnLock(event);
- // now run..
- if (func) {
- if (jsvIsFunction(func))
- jspExecuteFunction(&p, func, 0, 2, args);
- else if (jsvIsString(func))
- jsvUnLock(jspEvaluateVar(&p, func, 0));
- else
- jsError("Unknown type of callback in Event Queue");
- }
- //jsPrint("Event Done\n");
- jsvUnLock(func);
- jsvUnLock(args[0]);
- jsvUnLock(args[1]);
- }
- if (hasEvents) {
- jsiSetBusy(BUSY_INTERACTIVE, false);
- if (!wasInterrupted && jspIsInterrupted())
- interruptedDuringEvent = true;
- }
- }
- void jsiExecuteEventCallback(JsVar *callbackVar, JsVar *arg0, JsVar *arg1) { // array of functions or single function
- bool wasInterrupted = jspIsInterrupted();
- JsVar *callbackNoNames = jsvSkipName(callbackVar);
- if (callbackNoNames) {
- if (jsvIsArray(callbackNoNames)) {
- JsVarRef next = callbackNoNames->firstChild;
- while (next) {
- JsVar *child = jsvLock(next);
- jsiExecuteEventCallback(child, arg0, arg1);
- next = child->nextSibling;
- jsvUnLock(child);
- }
- } else if (jsvIsFunction(callbackNoNames)) {
- JsVar *args[2] = { arg0, arg1 };
- JsVar *parent = 0;
- jspExecuteFunction(&p, callbackNoNames, parent, 2, args);
- } else if (jsvIsString(callbackNoNames))
- jsvUnLock(jspEvaluateVar(&p, callbackNoNames, 0));
- else
- jsError("Unknown type of callback in Event Queue");
- jsvUnLock(callbackNoNames);
- }
- if (!wasInterrupted && jspIsInterrupted())
- interruptedDuringEvent = true;
- }
- bool jsiHasTimers() {
- if (!timerArray) return false;
- JsVar *timerArrayPtr = jsvLock(timerArray);
- JsVarInt c = jsvGetArrayLength(timerArrayPtr);
- jsvUnLock(timerArrayPtr);
- return c>0;
- }
- void jsiPrintUnregisteredMessage(const char *desc) {
- jsiConsolePrint("\n You must have registered Espruino in order to ");
- jsiConsolePrint(desc);
- jsiConsolePrint(".\n\n Please type register() for more information.\n\n");
- }
- void jsiIdle() {
- // This is how many times we have been here and not done anything.
- // It will be zeroed if we do stuff later
- if (loopsIdling<255) loopsIdling++;
- // Handle hardware-related idle stuff (like checking for pin events)
- bool wasBusy = false;
- IOEvent event;
- while (jshPopIOEvent(&event)) {
- jsiSetBusy(BUSY_INTERACTIVE, true);
- wasBusy = true;
- IOEventFlags eventType = IOEVENTFLAGS_GETTYPE(event.flags);
- loopsIdling = 0; // because we're not idling
- if (eventType == consoleDevice) {
- int i, c = IOEVENTFLAGS_GETCHARS(event.flags);
- jsiSetBusy(BUSY_INTERACTIVE, true);
- for (i=0;i<c;i++) jsiHandleChar(event.data.chars[i]);
- jsiSetBusy(BUSY_INTERACTIVE, false);
- }
- if (DEVICE_IS_USART(eventType)) {
- // ------------------------------------------------------------------------ SERIAL CALLBACK
- JsVar *usartClass = jsvSkipNameAndUnLock(jsiGetClassNameFromDevice(IOEVENTFLAGS_GETTYPE(event.flags)));
- if (usartClass) {
- JsVar *callback = jsvFindChildFromString(usartClass, USART_CALLBACK_NAME, false);
- if (callback) {
- int i, c = IOEVENTFLAGS_GETCHARS(event.flags);
- // Part of hackish solution to 7 bit support on STM32
- #ifdef STM32
- unsigned char bytesize = 8;
- unsigned char parity = 0;
- JsVar *options = jsvObjectGetChild(usartClass, DEVICE_OPTIONS_NAME, 0);
- if(jsvIsObject(options)) {
- bytesize = (unsigned char)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "bytesize", 0));
- JsVar *v = jsvObjectGetChild(options, "parity", 0);
- if(jsvIsString(v)) {
- parity = 0xFF;
- char s[8] = "";
- jsvGetString(v, s, sizeof(s) - 1);
- if(!strcmp(s, "o") || !strcmp(s, "odd")) {
- parity = 1;
- }
- else if(!strcmp(s, "e") || !strcmp(s, "even")) {
- parity = 2;
- }
- }
- else if(jsvIsInt(v)) {
- parity = (unsigned char)jsvGetInteger(v);
- }
- jsvUnLock(v);
- }
- jsvUnLock(options);
- #endif
- for (i=0; i<c; i++) {
- JsVar *data = jsvNewWithFlags(JSV_OBJECT);
- if (data) {
- JsVar *dataTime = jsvNewFromString("X");
- #ifdef STM32
- if(bytesize == 7 && parity > 0) {
- dataTime->varData.str[0] = event.data.chars[i] & 0x7F;
- }
- else if(bytesize == 8 && parity > 0) {
- dataTime->varData.str[0] = event.data.chars[i] & 0xFF;
- }
- else {
- dataTime->varData.str[0] = event.data.chars[i];
- }
- #else
- dataTime->varData.str[0] = event.data.chars[i];
- #endif
- if (dataTime) jsvUnLock(jsvAddNamedChild(data, dataTime, "data"));
- jsvUnLock(dataTime);
- }
-
- jsiExecuteEventCallback(callback, data, 0);
- jsvUnLock(data);
- }
- }
- jsvUnLock(callback);
- jsvUnLock(usartClass);
- }
- } else if (DEVICE_IS_EXTI(eventType)) { // ---------------------------------------------------------------- PIN WATCH
- // we have an event... find out what it was for...
- // Check everything in our Watch array
- JsVar *watchArrayPtr = jsvLock(watchArray);
- JsVarRef watch = watchArrayPtr->firstChild;
- while (watch) {
- JsVar *watchNamePtr = jsvLock(watch); // effectively the array index
- JsVar *watchPin = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
- Pin pin = jshGetPinFromVar(watchPin); // TODO: could be faster?
- jsvUnLock(watchPin);
- if (jshIsEventForPin(&event, pin)) {
- bool pinIsHigh = (event.flags&EV_EXTI_IS_HIGH)!=0;
- int watchEdge = (int)jsvGetIntegerAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "edge", false)));
- if (watchEdge==0 || (pinIsHigh && watchEdge>0) || (!pinIsHigh && watchEdge<0)) { // edge triggering
- JsVar *watchCallback = jsvFindChildFromStringRef(watchNamePtr->firstChild, "callback", false);
- bool watchRecurring = jsvGetBoolAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "recur", false)));
- JsVar *data = jsvNewWithFlags(JSV_OBJECT);
- if (data) {
- JsVar *dataTime = jsvNewFromFloat(jshGetMillisecondsFromTime(event.data.time)/1000);
- if (dataTime) jsvUnLock(jsvAddNamedChild(data, dataTime, "time"));
- jsvUnLock(dataTime);
- JsVar *dataState = jsvNewFromBool(pinIsHigh);
- if (dataState) jsvUnLock(jsvAddNamedChild(data, dataState, "state"));
- jsvUnLock(dataState);
- }
- jsiExecuteEventCallback(watchCallback, data, 0);
- jsvUnLock(data);
- if (!watchRecurring) {
- // free all
- jsvRemoveChild(watchArrayPtr, watchNamePtr);
- }
- jsvUnLock(watchCallback);
- }
- }
- watch = watchNamePtr->nextSibling;
- jsvUnLock(watchNamePtr);
- }
- jsvUnLock(watchArrayPtr);
- }
- }
- // Reset Flow control if it was set...
- if (jshGetEventsUsed() < IOBUFFER_XON) {
- int i;
- for (i=0;i<USARTS;i++)
- jshSetFlowControlXON(EV_SERIAL1+i, true);
- }
- // Check timers
- JsSysTime minTimeUntilNext = JSSYSTIME_MAX;
- JsSysTime time = jshGetSystemTime();
- JsVar *timerArrayPtr = jsvLock(timerArray);
- JsVarRef timer = timerArrayPtr->firstChild;
- while (timer) {
- JsVar *timerNamePtr = jsvLock(timer);
- timer = timerNamePtr->nextSibling; // ptr to next
- JsVar *timerTime = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "time", false));
- JsSysTime timeUntilNext = jsvGetInteger(timerTime) - time;
- if (timeUntilNext < minTimeUntilNext)
- minTimeUntilNext = timeUntilNext;
- if (timerTime && timeUntilNext<=0) {
- // we're now doing work
- jsiSetBusy(BUSY_INTERACTIVE, true);
- wasBusy = true;
- JsVar *timerCallback = jsvFindChildFromStringRef(timerNamePtr->firstChild, "callback", false);
- JsVar *timerRecurring = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "recur", false));
- JsVar *data = jsvNewWithFlags(JSV_OBJECT);
- if (data) {
- JsVar *dataTime = jsvNewFromFloat(jshGetMillisecondsFromTime(jsvGetInteger(timerTime))/1000);
- if (dataTime) jsvUnLock(jsvAddNamedChild(data, dataTime, "time"));
- jsvUnLock(dataTime);
- }
- jsiExecuteEventCallback(timerCallback, data, 0);
- jsvUnLock(data);
- if (jsvGetBool(timerRecurring)) {
- JsVarFloat interval = jsvGetFloatAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "interval", false)));
- if (interval<=0)
- jsvSetInteger(timerTime, time); // just set to current system time
- else
- jsvSetInteger(timerTime, jsvGetInteger(timerTime)+jshGetTimeFromMilliseconds(interval));
- } else {
- // free all
- JsVar *foundChild = jsvFindChildFromVar(timerArrayPtr, timerNamePtr, false);
- if (foundChild) {
- // check it exists - could have been removed during jsiExecuteEventCallback!
- jsvRemoveChild(timerArrayPtr, timerNamePtr);
- jsvUnLock(foundChild);
- }
- }
- jsvUnLock(timerCallback);
- jsvUnLock(timerRecurring);
- }
- jsvUnLock(timerTime);
- jsvUnLock(timerNamePtr);
- }
- jsvUnLock(timerArrayPtr);
- // Check for network events
- #ifdef USE_NET
- httpIdle();
- #endif
- // Just in case we got any events to do and didn't clear loopsIdling before
- if (wasBusy || !jsvArrayIsEmpty(events) )
- loopsIdling = 0;
- if (wasBusy)
- jsiSetBusy(BUSY_INTERACTIVE, false);
- // TODO: could now sort events by time?
- // execute any outstanding events
- if (!jspIsInterrupted()) {
- jsiExecuteEvents();
- }
- if (interruptedDuringEvent) {
- jspSetInterrupted(false);
- interruptedDuringEvent = false;
- jsiConsolePrint("Execution Interrupted during event processing - clearing all timers and watches.\n");
- jswrap_interface_clearInterval(0);
- jswrap_interface_clearWatch(0);
- }
- // check for TODOs
- if (todo) {
- jsiSetBusy(BUSY_INTERACTIVE, true);
- if (todo & TODO_RESET) {
- todo &= (TODOFlags)~TODO_RESET;
- // shut down everything and start up again
- jsiKill();
- jsiInit(false); // don't autoload
- }
- if (todo & TODO_FLASH_SAVE) {
- todo &= (TODOFlags)~TODO_FLASH_SAVE;
- jsvGarbageCollect(); // nice to have everything all tidy!
- jsiSoftKill();
- jspSoftKill(&p);
- jsvSoftKill();
- jshSaveToFlash();
- jsvSoftInit();
- jspSoftInit(&p);
- jsiSoftInit();
- }
- if (todo & TODO_FLASH_LOAD) {
- todo &= (TODOFlags)~TODO_FLASH_LOAD;
- jsiSoftKill();
- jspSoftKill(&p);
- jsvSoftKill();
- jshLoadFromFlash();
- jsvSoftInit();
- jspSoftInit(&p);
- jsiSoftInit();
- }
- jsiSetBusy(BUSY_INTERACTIVE, false);
- }
- /* if we've been around this loop, there is nothing to do, and
- * we have a spare 10ms then let's do some Garbage Collection
- * just in case. */
- if (loopsIdling==1 &&
- minTimeUntilNext > jshGetTimeFromMilliseconds(10)) {
- jsiSetBusy(BUSY_INTERACTIVE, true);
- jsvGarbageCollect();
- jsiSetBusy(BUSY_INTERACTIVE, false);
- }
- // Go to sleep!
- if (loopsIdling>1 && // once around the idle loop without having done any work already (just in case)
- #ifdef USB
- !jshIsUSBSERIALConnected() && // if USB is on, no point sleeping (later, sleep might be more drastic)
- #endif
- !jshHasEvents() && //no events have arrived in the mean time
- !jshHasTransmitData()/* && //nothing left to send over serial?
- minTimeUntilNext > SYSTICK_RANGE*5/4*/) { // we are sure we won't miss anything - leave a little leeway (SysTick will wake us up!)
- jshSleep(minTimeUntilNext);
- }
- }
- void jsiLoop() {
- // idle stuff for hardware
- jshIdle();
- // Do general idle stuff
- jsiIdle();
- // Idle LCD
- #ifdef USE_LCD
- graphicsIdle();
- #endif
-
- if (jspIsInterrupted()) {
- jspSetInterrupted(false);
- jsiConsoleRemoveInputLine();
- // clear input line
- jsvUnLock(inputLine);
- inputLine = jsvNewFromEmptyString();
- }
- // return console (if it was gone!)
- jsiReturnInputLine();
- }
- /** Output extra functions defined in an object such that they can be copied to a new device */
- void jsiDumpObjectState(JsVar *parentName, JsVar *parent) {
- JsVarRef childRef = parent->firstChild;
- while (childRef) {
- JsVar *child = jsvLock(childRef);
- JsVar *data = jsvSkipName(child);
- if (jsvIsStringEqual(child, JSPARSE_PROTOTYPE_VAR)) {
- JsVarRef protoRef = data->firstChild;
- while (protoRef) {
- JsVar *proto = jsvLock(protoRef);
- jsiConsolePrintStringVar(parentName);
- jsiConsolePrint(".prototype.");
- jsiConsolePrintStringVar(proto);
- jsiConsolePrint(" = ");
- JsVar *protoData = jsvSkipName(proto);
- jsfPrintJSON(protoData);
- jsvUnLock(protoData);
- jsiConsolePrint(";\n");
- protoRef = proto->nextSibling;
- jsvUnLock(proto);
- }
- } else {
- jsiConsolePrintStringVar(parentName);
- jsiConsolePrint(".");
- jsiConsolePrintStringVar(child);
- jsiConsolePrint(" = ");
- jsfPrintJSON(data);
- jsiConsolePrint(";\n");
- }
- jsvUnLock(data);
- childRef = child->nextSibling;
- jsvUnLock(child);
- }
- }
- /** Output current interpreter state such that it can be copied to a new device */
- void jsiDumpState() {
- JsVarRef childRef = p.root->firstChild;
- while (childRef) {
- JsVar *child = jsvLock(childRef);
- char childName[JSLEX_MAX_TOKEN_LENGTH];
- jsvGetString(child, childName, JSLEX_MAX_TOKEN_LENGTH);
- JsVar *data = jsvSkipName(child);
- if (jspIsCreatedObject(&p, data) || jswIsBuiltInObject(childName)) {
- jsiDumpObjectState(child, data);
- } else if (jsvIsStringEqual(child, JSI_TIMERS_NAME)) {
- // skip - done later
- } else if (jsvIsStringEqual(child, JSI_WATCHES_NAME)) {
- // skip - done later
- } else if (child->varData.str[0]==JS_HIDDEN_CHAR ||
- jshFromDeviceString(childName)!=EV_NONE) {
- // skip - don't care about this stuff
- } else if (!jsvIsNative(data)) { // just a variable/function!
- if (jsvIsFunction(data)) {
- // function-specific output
- jsiConsolePrint("function ");
- jsiConsolePrintStringVar(child);
- jsfPrintJSONForFunction(data);
- jsiConsolePrint("\n");
- // print any prototypes we had
- JsVar *proto = jsvObjectGetChild(data, JSPARSE_PROTOTYPE_VAR, 0);
- if (proto) {
- JsVarRef protoRef = proto->firstChild;
- jsvUnLock(proto);
- while (protoRef) {
- JsVar *protoName = jsvLock(protoRef);
- JsVar *protoData = jsvSkipName(protoName);
- jsiConsolePrintStringVar(child);
- jsiConsolePrint(".prototype.");
- jsiConsolePrintStringVar(protoName);
- jsiConsolePrint(" = ");
- jsfPrintJSON(protoData);
- jsiConsolePrint(";\n");
- jsvUnLock(protoData);
- protoRef = protoName->nextSibling;
- jsvUnLock(protoName);
- }
- }
- } else {
- // normal variable definition
- jsiConsolePrint("var ");
- jsiConsolePrintStringVar(child);
- jsiConsolePrint(" = ");
- jsfPrintJSON(data);
- jsiConsolePrint(";\n");
- }
- }
- jsvUnLock(data);
- childRef = child->nextSibling;
- jsvUnLock(child);
- }
- // Now do timers
- JsVar *timerArrayPtr = jsvLock(timerArray);
- JsVarRef timerRef = timerArrayPtr->firstChild;
- jsvUnLock(timerArrayPtr);
- while (timerRef) {
- JsVar *timerNamePtr = jsvLock(timerRef);
- JsVar *timerCallback = jsvSkipOneNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "callback", false));
- bool recur = jsvGetBoolAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "recur", false)));
- JsVar *timerInterval = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "interval", false));
- jsiConsolePrint(recur ? "setInterval(" : "setTimeout(");
- jsfPrintJSON(timerCallback);
- jsiConsolePrint(", ");
- jsfPrintJSON(timerInterval);
- jsiConsolePrint(");\n");
- jsvUnLock(timerInterval);
- jsvUnLock(timerCallback);
- // next
- timerRef = timerNamePtr->nextSibling;
- jsvUnLock(timerNamePtr);
- }
- // Now do watches
- {
- JsVar *watchArrayPtr = jsvLock(watchArray);
- JsVarRef watchRef = watchArrayPtr->firstChild;
- jsvUnLock(watchArrayPtr);
- while (watchRef) {
- JsVar *watchNamePtr = jsvLock(watchRef);
- JsVar *watchCallback = jsvSkipOneNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "callback", false));
- bool watchRecur = jsvGetBoolAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "recur", false)));
- int watchEdge = (int)jsvGetIntegerAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "edge", false)));
- JsVar *watchPin = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
- jsiConsolePrint("setWatch(");
- jsfPrintJSON(watchCallback);
- jsiConsolePrint(", ");
- jsfPrintJSON(watchPin);
- jsiConsolePrint(", { repeat:");
- jsiConsolePrint(watchRecur?"true":"false");
- jsiConsolePrint(", edge:");
- if (watchEdge<0) jsiConsolePrint("'falling'");
- else if (watchEdge>0) jsiConsolePrint("'rising'");
- else jsiConsolePrint("'both'");
- jsiConsolePrint(" });\n");
- jsvUnLock(watchPin);
- jsvUnLock(watchCallback);
- // next
- watchRef = watchNamePtr->nextSibling;
- jsvUnLock(watchNamePtr);
- }
- }
- // and now serial
- JsVar *str = jsvNewFromEmptyString();
- jsiAppendHardwareInitialisation(str, true);
- jsiConsolePrintStringVar(str);
- jsvUnLock(str);
- }
- void jsiSetTodo(TODOFlags newTodo) {
- todo = newTodo;
- }
- JsParse *jsiGetParser() {
- return &p;
- }
|