#include "mystdlib.h" #include "mytypes.h" #include "basic_tokens.h" #include "basic_tokenint.h" #include "basic_editor.h" #include "basic_utils.h" #include "basic_extern.h" #include "basic_textual.h" extern char mainState; extern token* toksBody; numeric* calcStack; short nextLineNum = 1; static prgline* progLine; short sp, spInit; varHolder* vars; char numVars; short arrayBytes; labelCacheElem* labelCache; short labelsCached; numeric lastDim; static numeric execStepsCount; static numeric delayT0, delayLimit; void execRem(void); void execPrint(void); void execInput(void); void execIf(void); void execGoto(void); void execGosub(void); void execReturn(void); void execEnd(void); void execLet(void); void execLeta(void); void execDim(void); void execDelay(void); void execData(void); void execEmit(void); void (*executors[])(void) = { execRem, execPrint, execInput, execIf, execGoto, execGosub, execReturn, execEnd, execLet, execLeta, execDim, execDelay, execData, execEmit, }; void resetTokenExecutor(void) { numVars = 0; arrayBytes = 0; sp = spInit; vars[0].name = 0; } short varSize(void) { return numVars * sizeof(varHolder) + arrayBytes; } void initTokenExecutor(char* space, short size) { spInit = (size / sizeof(*calcStack)); vars = (varHolder*)(void*)space; calcStack = (numeric*)(void*)space; resetTokenExecutor(); } short shortVarName(nstring* name) { short n = name->text[0]; if (name->len > 1) { n += name->text[1] * 127; } return n; } short shortArrayName(char letter) { return 0x7F00 | letter; } unsigned char findVar(short name) { short hi = numVars; short lo = 0; short mid; while (hi > lo) { mid = (hi + lo) / 2; if (vars[mid].name < name) { lo = mid + 1; } else { hi = mid; } } return lo; } numeric getVar(short name) { unsigned char i = findVar(name); return (vars[i].name == name) ? vars[i].value : 0; } short getArrayOffset(char letter) { short name = shortArrayName(letter); unsigned char i = findVar(name); return (vars[i].name == name) ? vars[i].value : -1; } char checkLowVarsMemory(short toAddBytes) { if (((char*)(vars + numVars)) + arrayBytes + toAddBytes >= (char*)(calcStack + spInit - 5)) { outputCr(); outputConstStr(ID_COMMON_STRINGS, 12, NULL); outputCr(); return 1; } return 0; } void setVar(short name, numeric value) { unsigned char i = findVar(name); if (vars[i].name != name) { if (checkLowVarsMemory(sizeof(varHolder))) { return; } if (i < numVars) { memmove(vars + i + 1, vars + i, sizeof(varHolder) * (numVars - i) + arrayBytes); } vars[i].name = name; numVars += 1; } vars[i].value = value; } short findLabel(short num) { short hi = labelsCached; short lo = 0; short mid; while (hi > lo) { mid = (hi + lo) / 2; if (labelCache[mid].num < num) { lo = mid + 1; } else { hi = mid; } } return lo; } prgline* getCachedLabel(short num) { short i = findLabel(num); return labelCache[i].num == num ? (prgline*)(void*)(prgStore + labelCache[i].offset) : NULL; } void addCachedLabel(short num, short offset) { short idx = findLabel(num); if (idx < labelsCached) { memmove(labelCache + idx + 1, labelCache + idx, sizeof(labelCacheElem) * (labelsCached - idx)); } labelCache[idx].num = num; labelCache[idx].offset = offset; labelsCached += 1; } static void advance(void) { if (curTok->type != TT_NONE) { curTok = nextToken(curTok); } } void calcOperation(char op) { numeric top = calcStack[sp++]; switch (op) { case '+': calcStack[sp] += top; break; case '-': calcStack[sp] -= top; break; case '*': calcStack[sp] *= top; break; case '/': calcStack[sp] /= top; break; case '%': calcStack[sp] %= top; break; case '~': calcStack[--sp] = -top; break; case '!': calcStack[--sp] = !top; break; case '<': calcStack[sp] = calcStack[sp] < top; break; case '>': calcStack[sp] = calcStack[sp] > top; break; case '=': calcStack[sp] = calcStack[sp] == top; break; case '{': calcStack[sp] = calcStack[sp] <= top; break; case '}': calcStack[sp] = calcStack[sp] >= top; break; case '#': calcStack[sp] = calcStack[sp] != top; break; case '&': calcStack[sp] = calcStack[sp] && top; break; case '|': calcStack[sp] = calcStack[sp] || top; break; case '^': calcStack[sp] = calcStack[sp] ^ top; break; } } void calcFunction(nstring* name) { short i; numeric r; numeric h = hashOfNStr(name); if (h == 0x1FF) { // KEY i = calcStack[sp]; calcStack[sp] = lastInput; if (i != 0) { lastInput = 0; } return; } if (h == 0xC9) { // MS calcStack[sp] = sysMillis(calcStack[sp]); return; } if (h == 0x1D3) { // ABS if (calcStack[sp] < 0) { calcStack[sp] = -calcStack[sp]; } return; } i = extraFunctionByHash(h); if (i >= 0) { // arguments will appear in reverse order r = extraFunction(i, calcStack + sp); sp += extraFuncArgCnt[i] - 1; calcStack[sp] = r; return; } calcStack[sp] = 0; } void calcArray(char letter) { short offset = getArrayOffset(letter); if (offset == -1) { calcStack[sp] = 0; return; } char b = (offset & 0x8000) ? 1 : sizeof(numeric); offset = (offset & 0x7FFF) + b * calcStack[sp]; char* p = ((char*)(void*)vars) + sizeof(varHolder) * numVars + offset; if (b > 1) { calcStack[sp] = *((numeric*)(void*)p); } else { calcStack[sp] = *((unsigned char*)(void*)p); } } numeric calcExpression(void) { while (1) { switch (curTok->type) { case TT_NONE: case TT_SEPARATOR: return calcStack[sp++]; case TT_NUMBER: calcStack[--sp] = curTok->body.integer; break; case TT_VARIABLE: calcStack[--sp] = getVar(shortVarName(&(curTok->body.str))); break; case TT_SYMBOL: calcOperation(curTok->body.symbol); break; case TT_FUNCTION: calcFunction(&(curTok->body.str)); break; case TT_ARRAY: calcArray(curTok->body.symbol); break; } advance(); } } void execLet(void) { short varname = shortVarName(&(curTok->body.str)); advance(); setVar(varname, calcExpression()); } void setArray(char symbol, short idx, numeric value) { short offset = getArrayOffset(symbol); if (offset == -1) { return; } char b = (offset & 0x8000) ? 1 : sizeof(numeric); offset = (offset & 0x7FFF) + b * idx; char* p = ((char*)(void*)vars) + sizeof(varHolder) * numVars + offset; if (b > 1) { *((numeric*)(void*)p) = value; } else { *((unsigned char*)(void*)p) = (value & 0xFF); } } void execLeta(void) { char a = curTok->body.symbol; advance(); short idx = calcExpression(); advance(); setArray(a, idx, calcExpression()); } void execDim(void) { short name = shortArrayName(curTok->body.symbol); lastDim = curTok->body.symbol & 0x1F; advance(); short len = curTok->body.integer; advance(); char itemSize; if (curTok->type == TT_NONE) { itemSize = sizeof(numeric); } else { advance(); itemSize = 1; } unsigned char pos = findVar(name); if (vars[pos].name == name) { return; } if (checkLowVarsMemory(sizeof(varHolder) + len * itemSize)) { return; } setVar(name, arrayBytes | (itemSize == 1 ? 0x8000 : 0)); arrayBytes += len * itemSize; } void execData(void) { char a = (lastDim & 0x1F) | 0x40; // capital letter unsigned char i; if (a < 'A' || a > 'Z') { return; } do { if (curTok->type == TT_NUMBER) { setArray(a, lastDim >> 5, curTok->body.integer); lastDim += (1 << 5); } else { for (i = 0; i < curTok->body.str.len; i += 1) { setArray(a, lastDim >> 5, curTok->body.str.text[i]); lastDim += (1 << 5); } } advance(); } while (curTok->type != TT_NONE); } void setDelay(numeric millis) { delayT0 = sysMillis(1); delayLimit = millis; } void execDelay(void) { setDelay(calcExpression()); mainState |= STATE_DELAY; } char checkDelay() { return sysMillis(1) - delayT0 > delayLimit; } void dispatchDelay() { if (checkDelay()) { mainState &= ~STATE_DELAY; } } void execRem(void) { while (curTok->type != TT_NONE) { advance(); } } void execPrint(void) { while (1) { switch (curTok->type) { case TT_NONE: outputCr(); return; case TT_SEPARATOR: break; case TT_LITERAL: outputNStr(&(curTok->body.str)); break; default: outputInt(calcExpression()); break; } advance(); } } void execInput(void) { mainState |= STATE_INPUT; outputChar('?'); outputChar(curTok->body.str.text[0]); outputChar('='); } void dispatchInput() { if (lastInput == 0) { return; } if (!readLine()) { return; } setVar(shortVarName(&(curTok->body.str)), decFromStr(lineSpace)); advance(); mainState &= ~STATE_INPUT; } void execEmit(void) { while (1) { switch (curTok->type) { case TT_NONE: return; case TT_SEPARATOR: break; default: outputChar(calcExpression() & 0xFF); break; } advance(); } } void execIf(void) { if (calcExpression() == 0) { while (curTok->type != TT_NONE) { advance(); } } else { advance(); } } void execGoto(void) { nextLineNum = curTok->body.integer; advance(); } void execGosub(void) { calcStack[--sp] = nextLineNum; nextLineNum = curTok->body.integer; advance(); } void execReturn(void) { nextLineNum = calcStack[sp++]; } void execEnd(void) { nextLineNum = 32767; } void execExtra(unsigned char cmd) { unsigned char n = extraCmdArgCnt[cmd]; char i; sp -= n; for (i = 0; i < n; i++) { calcStack[sp + i] = calcExpression(); advance(); } extraCommand(cmd, calcStack + sp); sp += n; } void executeTokens(token* t) { curTok = t; while (t->type != TT_NONE) { advance(); if (t->body.command < CMD_EXTRA) { executors[t->body.command](); if (t->body.command == CMD_INPUT) { break; } } else { execExtra(t->body.command - CMD_EXTRA); } t = curTok; } } void signalEndOfCode(void) { outputConstStr(ID_COMMON_STRINGS, 5, NULL); outputCr(); } void stopExecution() { if ((mainState & STATE_RUN) != 0) { editorLoad(); } mainState &= ~(STATE_RUN | STATE_STEPS | STATE_BREAK); } char executeStep() { prgline* p = findLine(nextLineNum); if (p->num == 0) { stopExecution(); signalEndOfCode(); return 1; } nextLineNum = p->num + 1; memcpy(lineSpace, p->str.text, p->str.len); lineSpace[p->str.len] = 0; parseLine(lineSpace, toksBody); executeTokens(toksBody); return 0; } void dispatchBreak() { stopExecution(); execStepsCount = 0; sp = spInit; outputConstStr(ID_COMMON_STRINGS, 4, NULL); // BREAK outputCr(); } void executeNonParsed(numeric count) { if (count != 0) { execStepsCount = count; return; } if (execStepsCount != -1) { execStepsCount -= 1; } if (executeStep()) { execStepsCount = 0; } if (execStepsCount == 0) { stopExecution(); } } void initParsedRun(void) { nextLineNum = 1; progLine = findLine(nextLineNum); labelsCached = 0; labelCache = (labelCacheElem*)(void*)(prgStore + prgSize); mainState |= STATE_RUN; } void executeParsedRun(void) { prgline* next; if (progLine->num == 0 || nextLineNum == 0) { stopExecution(); signalEndOfCode(); return; } next = (prgline*)(void*)((char*)(void*)progLine + sizeof(progLine->num) + sizeof(progLine->str.len) + progLine->str.len); nextLineNum = next->num; executeTokens((token*)(void*)(progLine->str.text)); if (next->num != nextLineNum) { progLine = getCachedLabel(nextLineNum); if (progLine == NULL) { progLine = findLine(nextLineNum); addCachedLabel(nextLineNum, (short)((char*)(void*)progLine - (char*)(void*)prgStore)); } } else { progLine = next; } }