diff options
| author | shaolin <[email protected]> | 2014-03-04 09:14:48 +0800 |
|---|---|---|
| committer | shaolin <[email protected]> | 2014-03-04 09:14:48 +0800 |
| commit | 020e95d91b5e1d7f902674a320e42657bee3ebfe (patch) | |
| tree | 65b65bfa527e0e8670dc807a8fee8d507d67ca48 /components/external/espruino/src/jsinteractive.c | |
| parent | 0fb9c54efda7fef4346523fdc4b92512a0441ee1 (diff) | |
| parent | 738871fd9ebbd53da6cfcc429dfdd5ffc17fd685 (diff) | |
Merge branch 'master' of https://github.com/RT-Thread/rt-thread
Diffstat (limited to 'components/external/espruino/src/jsinteractive.c')
| -rw-r--r-- | components/external/espruino/src/jsinteractive.c | 1686 |
1 files changed, 1686 insertions, 0 deletions
diff --git a/components/external/espruino/src/jsinteractive.c b/components/external/espruino/src/jsinteractive.c new file mode 100644 index 0000000000..9e63fed94e --- /dev/null +++ b/components/external/espruino/src/jsinteractive.c @@ -0,0 +1,1686 @@ +/* + * This file is part of Espruino, a JavaScript interpreter for Microcontrollers + * + * Copyright (C) 2013 Gordon Williams <[email protected]> + * + * 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; +} |
