void initBitlash(unsigned long baud) { #if defined(TINY_BUILD) beginSerial(9600); #else beginSerial(baud); #endif #if defined(ARM_BUILD) eeinit(); #endif initTaskList(); vinit(); displayBanner(); #if !defined(TINY_BUILD) // Run the script named "startup" if there is one strncpy_P(lbuf, getmsg(M_startup), STRVALLEN); // get the name "startup" in our cmd buf //if (findKey(lbuf) >= 0) doCommand(lbuf); // look it up. exists? call it. if (findscript(lbuf)) doCommand(lbuf); // look it up. exists? call it. #endif initlbuf(); }
///////// // // Parse and interpret a stream, and return its value // // This is used in doCommand to execute a passed-in or collected text command, // in domacrocommand() when a macro/function is called from within a parse stream, // and in runBackgroundTasks to kick off the background run. // // numvar execscript(byte scripttype, numvar scriptaddress, char *scriptname) { // save parse context parsepoint fetchmark; markparsepoint(&fetchmark); byte thesym = sym; vpush(symval); // if this is the first stream context in this invocation, // set up our error recovery point and init the value stack // otherwise we skip this to allow nested execution calls // to properly return to top // if (fetchtype == SCRIPT_NONE) { // Exceptions come here via longjmp; see bitlash-error.c switch(setjmp(env)) { case 0: break; case X_EXIT: { // POLICY: Stop all background tasks on any error // // It is not possible to be certain that continuing here will work. // Not all errors leave the interpreter in a working state. Though most do. // The conservative/deterministic choice is to stop all background tasks // and drop the user back to the command prompt. // // On the other hand, you may find this inconvenient in your application, // and may be happy taking the risk of continuing. // // In which case, comment out this line and proceed with caution. // // TODO: if the macro "onerror" exists, call it here instead. Let it "stop *". // // -br // initTaskList(); // stop all pending tasks #ifdef SOFTWARE_SERIAL_TX resetOutput(); // clean up print module #endif // Other cleanups here vinit(); // initialize the expression stack fetchtype = SCRIPT_NONE; // reset parse context fetchptr = 0L; // reset parse location // sd_up = 0; // TODO: reset file system return (numvar) -1; } // X_EXIT case } // switch } initparsepoint(scripttype, scriptaddress, scriptname); getsym(); // interpret the function text and collect its result numvar ret = getstatementlist(); returntoparsepoint(&fetchmark, 1); // now where were we? sym = thesym; symval = vpop(); return ret; }
void nukeeeprom(void) { initTaskList(); // stop any currently running background tasks int addr = STARTDB; while (addr <= ENDDB) { if (eeread(addr) != EMPTY) eewrite(addr, EMPTY); addr++; } }
/* * Handle the received message and implement the action. * * receivedMsgType the msgType, extracted from the * received mesage. * pReceivedMsgBody pointer to the body part of the * received message. * pSendMsg pointer to a message that we * can fill in with the response. * * @return SERVER_SUCCESS_KEEP_RUNNING unless * exitting in which case SERVER_EXIT_NORMALLY. */ static ServerReturnCode doAction (TaskHandlerMsgType receivedMsgType, UInt8 * pReceivedMsgBody, Msg *pSendMsg) { ServerReturnCode returnCode = SERVER_SUCCESS_KEEP_RUNNING; Bool success = false; ASSERT_PARAM (pReceivedMsgBody != PNULL, (unsigned long) pReceivedMsgBody); ASSERT_PARAM (pSendMsg != PNULL, (unsigned long) pSendMsg); /* We always respond with the same message type */ pSendMsg->msgType = (MsgType) receivedMsgType; /* Fill in the length so far */ pSendMsg->msgLength += sizeof (pSendMsg->msgType); /* Now handle each message specifically */ switch (receivedMsgType) { /* * Messages to do with the server itself */ case TASK_HANDLER_SERVER_START: { success = initTaskList(); } break; case TASK_HANDLER_SERVER_STOP: { success = clearTaskList(); returnCode = SERVER_EXIT_NORMALLY; } break; /* * Messages to do with tasks */ case TASK_HANDLER_NEW_TASK: { success = handleNewTaskReq ((RoboOneTaskReq *) pReceivedMsgBody); } break; case TASK_HANDLER_TICK: { success = tickTaskHandler(); } break; default: { ASSERT_ALWAYS_PARAM (receivedMsgType); } break; } /* Note: the following code assumes packing of 1 and that the start of a Cnf message contains the Bool 'success' */ pSendMsg->msgBody[0] = success; pSendMsg->msgLength += sizeof (Bool); return returnCode; }
void nukeeeprom(void) { initTaskList(); // stop any currently running background tasks int addr; for (addr = STARTDB; addr < ENDDB; addr++) { if (eeread(addr) != EMPTY) { eewrite(addr, EMPTY); } } }
/////////////////////// // handle a character from the input stream // may execute the command, etc. // void doCharacter(char c) { if (lbufptr >= &lbuf[LBUFLEN-1]) overflow(M_line); if ((c == '\r') || (c == '\n') || (c == '`')) { speol(); *lbufptr = 0; doCommand(lbuf); initlbuf(); } else if (c == 3) { // ^C break/stop msgpl(M_ctrlc); initTaskList(); initlbuf(); } else if (c == 2) { // ^B suspend Background macros suspendBackground = !suspendBackground; } else if ((c == 8) || (c == 0x7f)) { if (lbufptr == lbuf) spb(7); // bell else { spb(8); spb(' '); spb(8); *(--lbufptr) = 0; } } #ifdef PARSER_TRACE else if (c == 20) { // ^T toggle trace trace = !trace; spb(7); } #endif else if (c == 21) { // ^U to get last line msgpl(M_ctrlu); prompt(); sp(lbuf); lbufptr = lbuf + strlen(lbuf); } else { spb(c); *lbufptr++ = c; } }
// Get a statement void getstatement(void) { #if !defined(TINY85) chkbreak(); #endif if (sym == s_while) { // at this point sym is pointing at s_while, before the conditional expression // save fetchptr so we can restart parsing from here as the while iterates char *fetchmark = fetchptr; for (;;) { fetchptr = fetchmark; // restore to mark primec(); // set up for mr. getsym() getsym(); // fetch the start of the conditional if (!getnum()) { //longjmp(env, X_EXIT); // get the conditional; exit on false sym = s_eof; // we're finished here. move along. return; } if (sym != s_colon) expectedchar(':'); getsym(); // eat : getstatementlist(); } } else if (sym == s_if) { getsym(); // fetch the start of the conditional if (!getnum()) { //longjmp(env, X_EXIT); // get the conditional; exit on false sym = s_eof; return; } if (sym != s_colon) expectedchar(':'); getsym(); // eat : getstatementlist(); } #if SKETCH // The switch statement: call one of N macros based on a selector value // switch <numval>: macroid1, macroid2,.., macroidN // numval < 0: numval = 0 // numval > N: numval = N else if (sym == s_switch) { getsym(); // eat "switch" numvar selector = getnum(); // evaluate the switch value if (selector < 0) selector = 0; if (sym != s_colon) expectedchar(':'); // we sit before the first macroid // scan and discard the <selector>'s worth of macro ids // that sit before the one we want for (;;) { getsym(); // get an id, sets symval to its eeprom addr as a side effect if (sym != s_macro) expected (6); // TODO: define M_macro instead of 6 getsym(); // eat id, get separator; assume symval is untouched if ((sym == s_semi) || (sym == s_eof)) break; // last case is default so we exit always if (sym != s_comma) expectedchar(','); if (!selector) break; // ok, this is the one we want to execute selector--; // one down... } // call the macro whose addr is squirreled in symval all this time // on return, the parser is ready to pick up where we left off doMacroCall(symval); // scan past the rest of the unused switch options, if any // TODO: syntax checking for non-chosen options could be made much tighter at the cost of some space while ((sym != s_semi) && (sym != s_eof)) getsym(); // scan to end of statement without executing } #endif else if ((sym == s_macro) || (sym == s_undef)) { // macro def or ref getsym(); // scan past macro name to next symbol: ; or := if (sym == s_define) { // macro definition: macroid := strvalue // to define the macro, we need to copy the id somewhere on the stack // to avoid having this local buffer in every getstatement stack frame, // we break out defineMacro here to a separate function that only eats that // stack in the case that a macro is being defined #ifdef TINY85 unexpected(M_defmacro); #else defineMacro(); #endif } else if ((sym == s_semi) || (sym == s_eof)) { // valid macro reference: let's call it #if SKETCH doMacroCall(symval); // parseid stashes the macro address in symval #else char op = sym; // save sym for restore expval = findKey(idbuf); // assumes id in idbuf isn't clobbered since getsym() above if (expval >= 0) { char *fetchmark = fetchptr; // save the current parse pointer // call the macro calleeprommacro(findend(expval)); // register the macro into the parser stream getsym(); getstatementlist(); // parse and execute the macro code here if (sym != s_eof) expected(M_eof); // restore parsing context so we can resume cleanly fetchptr = fetchmark; // restore pointer primec(); // and inchar sym = op; // restore saved sym: s_semi or s_eof } else unexpected(M_id); #endif } else expectedchar(';'); //else getexpression(); // assume it was macro1+32+macro2... } else if (sym == s_run) { // run macroname getsym(); if (sym != s_macro) unexpected(M_id); #if 0 // address of macroid is in symval via parseid startTask(kludge(symval)); getsym(); #else // address of macroid is in symval via parseid // check for [,snoozeintervalms] getsym(); // eat macroid to check for comma; symval untouched if (sym == s_comma) { vpush(symval); getsym(); // eat the comma getnum(); // get a number or else startTask(kludge(vpop()), expval); } else startTask(kludge(symval), 0); #endif } else if (sym == s_stop) { getsym(); if (sym == s_mul) { // stop * stops all tasks initTaskList(); getsym(); } else if ((sym == s_semi) || (sym == s_eof)) { if (background) stopTask(curtask); // stop with no args stops the current task IF we're in back else initTaskList(); // in foreground, stop all } else stopTask(getnum()); } else if (sym == s_boot) reboot(); #if !defined(TINY85) else if (sym == s_rm) { // rm "sym" or rm * getsym(); if (sym == s_macro) { eraseentry(idbuf); } else if (sym == s_mul) nukeeeprom(); else expected(M_id); getsym(); } else if (sym == s_ps) showTaskList(); else if (sym == s_peep) { getsym(); cmd_peep(); } else if (sym == s_ls) { getsym(); cmd_ls(); } else if (sym == s_help) { getsym(); cmd_help(); } else if (sym == s_print) { getsym(); cmd_print(); } #endif #ifdef HEX_UPLOAD // a line beginning with a colon is treated as a hex record // containing data to upload to eeprom // // TODO: verify checksum // else if (sym == s_colon) { // fetchptr points at the byte count byte byteCount = gethex(2); // 2 bytes byte count int addr = gethex(4); // 4 bytes address byte recordType = gethex(2); // 2 bytes record type; now fetchptr -> data if (recordType == 1) reboot(); // reboot on EOF record (01) if (recordType != 0) return; // we only handle the data record (00) if (addr == 0) nukeeeprom(); // auto-clear eeprom on write to 0000 while (byteCount--) eewrite(addr++, gethex(2)); // update the eeprom gethex(2); // discard the checksum getsym(); // and re-prime the parser } #endif else { getexpression(); } }
// Get a statement numvar getstatement(void) { numvar retval = 0; //char *fetchmark; numvar fetchmark; chkbreak(); if (sym == s_while) { // at this point sym is pointing at s_while, before the conditional expression // save fetchptr so we can restart parsing from here as the while iterates //fetchmark = fetchptr; fetchmark = markparsepoint(); for (;;) { //fetchptr = fetchmark; // restore to mark //primec(); // set up for mr. getsym() returntoparsepoint(fetchmark, 0); getsym(); // fetch the start of the conditional if (getnum()) { retval = getstatement(); if (sym == s_returning) break; // exit if we caught a return } else { skipstatement(); break; } } } else if (sym == s_if) { getsym(); // eat "if" if (getnum()) { retval = getstatement(); if (sym == s_else) { getsym(); // eat "else" skipstatement(); } } else { skipstatement(); if (sym == s_else) { getsym(); // eat "else" retval = getstatement(); } } } else if (sym == s_lcurly) { getsym(); // eat "{" while ((sym != s_eof) && (sym != s_returning) && (sym != s_rcurly)) retval = getstatement(); if (sym == s_rcurly) getsym(); // eat "}" } else if (sym == s_return) { getsym(); // eat "return" if ((sym != s_eof) && (sym != s_semi)) retval = getnum(); sym = s_returning; // signal we're returning up the line } else if (sym == s_switch) retval = getswitchstatement(); else if (sym == s_function) cmd_function(); else if (sym == s_run) { // run macroname getsym(); if ((sym != s_script_eeprom) && (sym != s_script_progmem) && (sym != s_script_file)) unexpected(M_id); // address of macroid is in symval via parseid // check for [,snoozeintervalms] getsym(); // eat macroid to check for comma; symval untouched if (sym == s_comma) { vpush(symval); getsym(); // eat the comma getnum(); // get a number or else startTask(vpop(), expval); } else startTask(symval, 0); } else if (sym == s_stop) { getsym(); if (sym == s_mul) { // stop * stops all tasks initTaskList(); getsym(); } else if ((sym == s_semi) || (sym == s_eof)) { if (background) stopTask(curtask); // stop with no args stops the current task IF we're in back else initTaskList(); // in foreground, stop all } else stopTask(getnum()); } else if (sym == s_boot) reboot(); else if (sym == s_rm) { // rm "sym" or rm * getsym(); if (sym == s_script_eeprom) { eraseentry(idbuf); } else if (sym == s_mul) nukeeeprom(); else if (sym != s_undef) expected(M_id); getsym(); } else if (sym == s_ps) { getsym(); showTaskList(); } else if (sym == s_peep) { getsym(); cmd_peep(); } else if (sym == s_ls) { getsym(); cmd_ls(); } else if (sym == s_help) { getsym(); cmd_help(); } else if (sym == s_print) { getsym(); cmd_print(); } else if (sym == s_semi) { ; } // ;) #ifdef HEX_UPLOAD // a line beginning with a colon is treated as a hex record // containing data to upload to eeprom // // TODO: verify checksum // else if (sym == s_colon) { // fetchptr points at the byte count byte byteCount = gethex(2); // 2 bytes byte count int addr = gethex(4); // 4 bytes address byte recordType = gethex(2); // 2 bytes record type; now fetchptr -> data if (recordType == 1) reboot(); // reboot on EOF record (01) if (recordType != 0) return; // we only handle the data record (00) if (addr == 0) nukeeeprom(); // auto-clear eeprom on write to 0000 while (byteCount--) eewrite(addr++, gethex(2)); // update the eeprom gethex(2); // discard the checksum getsym(); // and re-prime the parser } #endif else getexpression(); if (sym == s_semi) getsym(); // eat trailing ';' return retval; }
int initSignal(signal_t *p_signal) { return initTaskList(&(p_signal->queue)); }
// Get a statement numvar getstatement(void) { numvar retval = 0; char *fetchmark; chkbreak(); //#define LINEMODE #ifdef LINEMODE if (sym == s_while) { // at this point sym is pointing at s_while, before the conditional expression // save fetchptr so we can restart parsing from here as the while iterates char *fetchmark = fetchptr; for (;;) { fetchptr = fetchmark; // restore to mark primec(); // set up for mr. getsym() getsym(); // fetch the start of the conditional if (!getnum()) { //longjmp(env, X_EXIT); // get the conditional; exit on false sym = s_eof; // we're finished here. move along. return; } if (sym != s_colon) expectedchar(':'); getsym(); // eat : getstatementlist(); } } else if (sym == s_if) { getsym(); // fetch the start of the conditional if (!getnum()) { //longjmp(env, X_EXIT); // get the conditional; exit on false sym = s_eof; return; } if (sym != s_colon) expectedchar(':'); getsym(); // eat : getstatementlist(); } // The switch statement: call one of N macros based on a selector value // switch <numval>: macroid1, macroid2,.., macroidN // numval < 0: numval = 0 // numval > N: numval = N else if (sym == s_switch) { getsym(); // eat "switch" numvar selector = getnum(); // evaluate the switch value if (selector < 0) selector = 0; if (sym != s_colon) expectedchar(':'); // we sit before the first macroid // scan and discard the <selector>'s worth of macro ids // that sit before the one we want for (;;) { getsym(); // get an id, sets symval to its eeprom addr as a side effect if (sym != s_macro) expected (6); // TODO: define M_macro instead of 6 getsym(); // eat id, get separator; assume symval is untouched if ((sym == s_semi) || (sym == s_eof)) break; // last case is default so we exit always if (sym != s_comma) expectedchar(','); if (!selector) break; // ok, this is the one we want to execute selector--; // one down... } // call the macro whose addr is squirreled in symval all this time // on return, the parser is ready to pick up where we left off domacrocall(symval); // scan past the rest of the unused switch options, if any // TODO: syntax checking for non-chosen options could be made much tighter at the cost of some space while ((sym != s_semi) && (sym != s_eof)) getsym(); // scan to end of statement without executing } #else // new statement handling if (sym == s_while) { // at this point sym is pointing at s_while, before the conditional expression // save fetchptr so we can restart parsing from here as the while iterates fetchmark = fetchptr; for (;;) { fetchptr = fetchmark; // restore to mark primec(); // set up for mr. getsym() getsym(); // fetch the start of the conditional if (getnum()) { retval = getstatement(); if (sym == s_returning) break; // exit if we caught a return } else { skipstatement(); break; } } } else if (sym == s_if) { getsym(); // eat "if" if (getnum()) { retval = getstatement(); if (sym == s_else) { getsym(); // eat "else" skipstatement(); } } else { skipstatement(); if (sym == s_else) { getsym(); // eat "else" retval = getstatement(); } } } else if (sym == s_lcurly) { getsym(); // eat "{" while ((sym != s_eof) && (sym != s_returning) && (sym != s_rcurly)) retval = getstatement(); if (sym == s_rcurly) getsym(); // eat "}" } else if (sym == s_return) { getsym(); // eat "return" if ((sym != s_eof) && (sym != s_semi)) retval = getnum(); sym = s_returning; // signal we're returning up the line } else if (sym == s_switch) retval = getswitchstatement(); else if (sym == s_function) cmd_function(); #endif else if (sym == s_run) { // run macroname getsym(); if (sym != s_macro) unexpected(M_id); // address of macroid is in symval via parseid // check for [,snoozeintervalms] getsym(); // eat macroid to check for comma; symval untouched if (sym == s_comma) { vpush(symval); getsym(); // eat the comma getnum(); // get a number or else startTask(kludge(vpop()), expval); } else startTask(kludge(symval), 0); } else if (sym == s_stop) { getsym(); if (sym == s_mul) { // stop * stops all tasks initTaskList(); getsym(); } else if ((sym == s_semi) || (sym == s_eof)) { if (background) stopTask(curtask); // stop with no args stops the current task IF we're in back else initTaskList(); // in foreground, stop all } else stopTask(getnum()); } else if (sym == s_boot) reboot(); #if !defined(TINY85) else if (sym == s_rm) { // rm "sym" or rm * getsym(); if (sym == s_macro) { eraseentry(idbuf); } else if (sym == s_mul) nukeeeprom(); else if (sym != s_undef) expected(M_id); getsym(); } else if (sym == s_ps) { getsym(); showTaskList(); } else if (sym == s_peep) { getsym(); cmd_peep(); } else if (sym == s_ls) { getsym(); cmd_ls(); } else if (sym == s_help) { getsym(); cmd_help(); } else if (sym == s_print) { getsym(); cmd_print(); } else if (sym == s_semi) { ; } // ;) #endif #ifdef HEX_UPLOAD // a line beginning with a colon is treated as a hex record // containing data to upload to eeprom // // TODO: verify checksum // else if (sym == s_colon) { // fetchptr points at the byte count byte byteCount = gethex(2); // 2 bytes byte count int addr = gethex(4); // 4 bytes address byte recordType = gethex(2); // 2 bytes record type; now fetchptr -> data if (recordType == 1) reboot(); // reboot on EOF record (01) if (recordType != 0) return; // we only handle the data record (00) if (addr == 0) nukeeeprom(); // auto-clear eeprom on write to 0000 while (byteCount--) eewrite(addr++, gethex(2)); // update the eeprom gethex(2); // discard the checksum getsym(); // and re-prime the parser } #endif else getexpression(); if (sym == s_semi) getsym(); // eat trailing ';' return retval; }