Files
zSoft/apps/tbasic/basic_exectoks.c

594 lines
13 KiB
C

#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;
}
}