First push after merge, split and enhance both zOS and zputa
This commit is contained in:
69
apps/tbasic/Makefile
Executable file
69
apps/tbasic/Makefile
Executable file
@@ -0,0 +1,69 @@
|
||||
#########################################################################################################
|
||||
##
|
||||
## Name: Makefile
|
||||
## Created: July 2019
|
||||
## Author(s): Philip Smart
|
||||
## Description: App Makefile - Build an App for the ZPU Test Application (zputa) or the zOS
|
||||
## operating system.
|
||||
## This makefile builds an app which is stored on an SD card and called by ZPUTA/zOS
|
||||
## The app is for testing some component where the code is not built into ZPUTA or
|
||||
## a user application for zOS.
|
||||
##
|
||||
## Credits:
|
||||
## Copyright: (c) 2019-20 Philip Smart <philip.smart@net2net.org>
|
||||
##
|
||||
## History: July 2019 - Initial Makefile created for template use.
|
||||
## April 2020 - Added K64F as an additional target and resplit ZPUTA into zOS.
|
||||
##
|
||||
## Notes: Optional component enables:
|
||||
## USELOADB - The Byte write command is implemented in hw#sw so use it.
|
||||
## USE_BOOT_ROM - The target is ROM so dont use initialised data.
|
||||
## MINIMUM_FUNTIONALITY - Minimise functionality to limit code size.
|
||||
##
|
||||
#########################################################################################################
|
||||
## This source file is free software: you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published
|
||||
## by the Free Software Foundation, either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This source file is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#########################################################################################################
|
||||
|
||||
APP_NAME = tbasic
|
||||
APP_DIR = $(CURDIR)/..
|
||||
APP_COMMON_DIR = $(CURDIR)/../common
|
||||
BASEDIR = ../../..
|
||||
|
||||
# Override values given by parent make for this application as its memory usage differs from the standard app.
|
||||
override HEAPADDR = 0x1FFF5000
|
||||
override HEAPSIZE = 0x00033000
|
||||
override STACKADDR = 0x20028000
|
||||
override STACKSIZE = 0x00000000
|
||||
|
||||
# Modules making up Kilo.
|
||||
APP_C_SRC = basic_main.c basic_editor.c basic_exectoks.c basic_expr.c basic_tokens.c basic_utils.c $(APP_COMMON_DIR)/sysutils.c
|
||||
CFLAGS =
|
||||
CPPFLAGS = -D__HEAPADDR__=$(HEAPADDR) -D__HEAPSIZE__=$(HEAPSIZE)
|
||||
LDFLAGS = -nostdlib
|
||||
|
||||
# Filter out the standard HEAP address and size, replacing with the ones required for this application.
|
||||
# Useful for sub-makes
|
||||
FILTER1 = $(filter-out $(filter HEAPADDR=%,$(MAKEFLAGS)), $(MAKEFLAGS))
|
||||
FILTER2 = $(filter-out $(filter HEAPSIZE=%,$(FILTER1)), $(FILTER1))
|
||||
NEWMAKEFLAGS = $(FILTER2) HEAPADDR=$(HEADADDR) HEAPSIZE=$(HEAPSIZE)
|
||||
|
||||
# Modules making up BASIC.
|
||||
CFLAGS =
|
||||
CPPFLAGS =
|
||||
|
||||
ifeq ($(__K64F__),1)
|
||||
include $(APP_DIR)/Makefile.k64f
|
||||
else
|
||||
include $(APP_DIR)/Makefile.zpu
|
||||
endif
|
||||
133
apps/tbasic/basic_editor.c
Normal file
133
apps/tbasic/basic_editor.c
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "mystdlib.h"
|
||||
#include "basic_editor.h"
|
||||
#include "basic_utils.h"
|
||||
#include "basic_tokens.h"
|
||||
#include "basic_extern.h"
|
||||
#include "basic_textual.h"
|
||||
|
||||
extern token* toksBody;
|
||||
char* prgStore;
|
||||
short prgSize;
|
||||
static short maxProgSize;
|
||||
static unsigned char lineSpacePos;
|
||||
char lastInput;
|
||||
|
||||
void resetEditor(void) {
|
||||
((prgline*)prgStore)->num = 0;
|
||||
prgSize = 2;
|
||||
lineSpacePos = 0;
|
||||
}
|
||||
|
||||
void initEditor(char* prgBody, short progSpaceSize) {
|
||||
maxProgSize = progSpaceSize;
|
||||
prgStore = prgBody;
|
||||
resetEditor();
|
||||
}
|
||||
|
||||
char readLine() {
|
||||
if (lastInput == '\r' || lastInput == '\n') {
|
||||
trim(lineSpace);
|
||||
lineSpace[lineSpacePos] = 0;
|
||||
lineSpacePos = 0;
|
||||
sysEcho('\n');
|
||||
return 1;
|
||||
} else if (lastInput == '\b' || lastInput == 127) {
|
||||
if (lineSpacePos > 0) {
|
||||
lastInput = '\b';
|
||||
lineSpacePos -= 1;
|
||||
}
|
||||
} else if (lastInput >= ' ') {
|
||||
lineSpace[lineSpacePos++] = lastInput;
|
||||
}
|
||||
sysEcho(lastInput);
|
||||
return 0;
|
||||
}
|
||||
|
||||
short lineSize(prgline* p) {
|
||||
return p->str.len + 3;
|
||||
}
|
||||
|
||||
prgline* nextLine(prgline* p) {
|
||||
return (prgline*)(void*)((char*)(void*)p + lineSize(p));
|
||||
}
|
||||
|
||||
prgline* findLine(short num) {
|
||||
prgline* p = (prgline*)(void*)prgStore;
|
||||
while (p->num != 0 && p->num < num) {
|
||||
p = nextLine(p);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void injectLine(char* s, short num) {
|
||||
unsigned char len;
|
||||
prgline* p = findLine(num);
|
||||
if (p->num == num) {
|
||||
len = (char*)(void*)nextLine(p) - (char*)(void*)p;
|
||||
memmove(p, nextLine(p), prgStore + prgSize - (char*)(void*)nextLine(p));
|
||||
prgSize -= len;
|
||||
}
|
||||
len = strlen(s);
|
||||
if (prgSize + len + 3 >= maxProgSize) {
|
||||
outputCr();
|
||||
outputConstStr(ID_COMMON_STRINGS, 13, NULL);
|
||||
outputCr();
|
||||
return;
|
||||
}
|
||||
if (len > 0) {
|
||||
memmove((char*)(void*)p + len + 3, p, prgStore + prgSize - (char*)(void*)p);
|
||||
prgSize += len + 3;
|
||||
p->num = num;
|
||||
p->str.len = len;
|
||||
memcpy(p->str.text, s, len);
|
||||
}
|
||||
}
|
||||
|
||||
char editorSave(void) {
|
||||
if (!storageOperation(NULL, 1)) {
|
||||
return 0;
|
||||
}
|
||||
storageOperation(&prgSize, sizeof(prgSize));
|
||||
storageOperation(prgStore, prgSize);
|
||||
storageOperation(NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char editorLoad(void) {
|
||||
if (!storageOperation(NULL, -1)) {
|
||||
return 0;
|
||||
}
|
||||
storageOperation(&prgSize, (short) -sizeof(prgSize));
|
||||
storageOperation(prgStore, -prgSize);
|
||||
storageOperation(NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char editorLoadParsed() {
|
||||
void* p = prgStore;
|
||||
unsigned char len;
|
||||
if (!storageOperation(NULL, -1)) {
|
||||
return 0;
|
||||
}
|
||||
storageOperation(lineSpace, -2);
|
||||
while (1) {
|
||||
storageOperation(p, (short) -sizeof(short));
|
||||
if (*((short*)p) == 0) {
|
||||
break;
|
||||
}
|
||||
parseLine(lineSpace, toksBody);
|
||||
p = (char*)p + sizeof(short);
|
||||
storageOperation(&len, (short) -sizeof(len));
|
||||
storageOperation(lineSpace, -len);
|
||||
lineSpace[len] = 0;
|
||||
parseLine(lineSpace, toksBody);
|
||||
len = tokenChainSize(toksBody);
|
||||
*((char*)p) = len;
|
||||
memcpy((char*)p + 1, toksBody, len);
|
||||
p = (char*)p + len + 1;
|
||||
}
|
||||
storageOperation(NULL, 0);
|
||||
prgSize = ((char*)p - (char*)(void*)prgStore) + sizeof(short);
|
||||
return 1;
|
||||
}
|
||||
|
||||
19
apps/tbasic/basic_editor.h
Normal file
19
apps/tbasic/basic_editor.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef __EDITOR_H_
|
||||
#define __EDITOR_H_
|
||||
|
||||
#include "basic_utils.h"
|
||||
|
||||
extern char* prgStore;
|
||||
extern short prgSize;
|
||||
|
||||
void resetEditor(void);
|
||||
void initEditor(char* prgBody, short progSpaceSize);
|
||||
char readLine();
|
||||
prgline* findLine(short num);
|
||||
void injectLine(char* s, short num);
|
||||
char editorSave(void);
|
||||
char editorLoad(void);
|
||||
char editorLoadParsed(void);
|
||||
|
||||
#endif
|
||||
|
||||
593
apps/tbasic/basic_exectoks.c
Normal file
593
apps/tbasic/basic_exectoks.c
Normal file
@@ -0,0 +1,593 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
25
apps/tbasic/basic_exectoks.h
Normal file
25
apps/tbasic/basic_exectoks.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef __EXECTOKS_H_
|
||||
#define __EXECTOKS_H_
|
||||
|
||||
#include "basic_tokens.h"
|
||||
|
||||
extern short nextLineNum;
|
||||
|
||||
void resetTokenExecutor(void);
|
||||
void initTokenExecutor(char* space, short size);
|
||||
short varSize(void);
|
||||
void executeTokens(token* t);
|
||||
char executeStep(char* lineBuf, token* tokenBuf);
|
||||
void execBreak(void);
|
||||
void executeNonParsed(numeric count);
|
||||
void initParsedRun(void);
|
||||
void executeParsedRun(void);
|
||||
void setLastInput(short c);
|
||||
void dispatchInput(void);
|
||||
void dispatchDelay(void);
|
||||
void setDelay(numeric millis);
|
||||
char checkDelay();
|
||||
void dispatchBreak(void);
|
||||
|
||||
#endif
|
||||
|
||||
232
apps/tbasic/basic_expr.c
Normal file
232
apps/tbasic/basic_expr.c
Normal file
@@ -0,0 +1,232 @@
|
||||
#include "mystdlib.h"
|
||||
#include "basic_tokens.h"
|
||||
#include "basic_tokenint.h"
|
||||
#include "basic_expr.h"
|
||||
|
||||
char parseExprUnary() {
|
||||
char c = *getCurTokPos();
|
||||
if (c != '-' && c != '!') {
|
||||
return 0;
|
||||
}
|
||||
parseSymbol();
|
||||
if (prevTok->body.symbol == '-') {
|
||||
prevTok->body.symbol = '~';
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
char parseExprVal(void) {
|
||||
if (parseNumber()) {
|
||||
if (*getCurTokPos() == '(') {
|
||||
setTokenError(getCurTokPos(), 9);
|
||||
return 'e';
|
||||
}
|
||||
return '+';
|
||||
}
|
||||
if (parseName(0)) {
|
||||
if (*getCurTokPos() == '(') {
|
||||
prevTok->type = TT_FUNCTION;
|
||||
skipTokenInInput(1);
|
||||
return 'f';
|
||||
}
|
||||
prevTok->type = TT_VARIABLE;
|
||||
return '+';
|
||||
}
|
||||
if (*getCurTokPos() == '(') {
|
||||
parseSymbol();
|
||||
return '(';
|
||||
}
|
||||
if (parseExprUnary()) {
|
||||
return '1';
|
||||
}
|
||||
setTokenError(getCurTokPos(), 9);
|
||||
return 'e';
|
||||
}
|
||||
|
||||
char parseExprBop(void) {
|
||||
char c = *getCurTokPos();
|
||||
if (c == 0 || c == ';') {
|
||||
return 's';
|
||||
}
|
||||
if (c == ')') {
|
||||
return ')';
|
||||
}
|
||||
if (c == ',') {
|
||||
return ',';
|
||||
}
|
||||
if (c == '+' || c == '-' || c == '*' || c == '^' || c == '/' || c == '%'
|
||||
|| c == '<' || c == '>' || c == '=' || c == '&' || c == '|') {
|
||||
parseSymbol();
|
||||
return '1';
|
||||
}
|
||||
setTokenError(getCurTokPos(), 9);
|
||||
return 'e';
|
||||
}
|
||||
|
||||
char parseExprRbr(char brCount, char argCount) {
|
||||
if (brCount < 1) {
|
||||
setTokenError(getCurTokPos(), 9);
|
||||
return 'e';
|
||||
} else {
|
||||
parseSymbol();
|
||||
if (argCount > 0) {
|
||||
prevTok->type = TT_FUNC_END;
|
||||
prevTok->body.symbol = argCount;
|
||||
}
|
||||
return '+';
|
||||
}
|
||||
}
|
||||
|
||||
char parseExprComma(char inFunc) {
|
||||
if (inFunc == 0) {
|
||||
setTokenError(getCurTokPos(), 9);
|
||||
return 'e';
|
||||
} else {
|
||||
parseSymbol();
|
||||
return '1';
|
||||
}
|
||||
}
|
||||
|
||||
schar operatorPriority(char op) {
|
||||
switch (op) {
|
||||
case '&':
|
||||
case '|':
|
||||
return 5;
|
||||
case '<':
|
||||
case '>':
|
||||
case '=':
|
||||
case '#':
|
||||
case '{':
|
||||
case '}':
|
||||
return 10;
|
||||
case '+':
|
||||
case '-':
|
||||
return 15;
|
||||
case '*':
|
||||
case '/':
|
||||
case '%':
|
||||
return 20;
|
||||
case '!':
|
||||
case '~':
|
||||
return 25;
|
||||
case '(':
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
char isUnary(char op) {
|
||||
return op == '!' || op == '~';
|
||||
}
|
||||
|
||||
char convertRpnPop(char op) {
|
||||
if (op == '(') {
|
||||
return 0;
|
||||
}
|
||||
curTok->type = TT_SYMBOL;
|
||||
curTok->body.symbol = op;
|
||||
curTok = nextToken(curTok);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void shuntingYard(token* next) {
|
||||
char opstack[16];
|
||||
schar sp = -1;
|
||||
char prio;
|
||||
char op;
|
||||
char* start = (char*)(void*)next;
|
||||
while (next->type != TT_ERROR) {
|
||||
if (next->type == TT_VARIABLE || next->type == TT_NUMBER) {
|
||||
copyToken(curTok, next);
|
||||
curTok = nextToken(curTok);
|
||||
} else if (next->type == TT_SYMBOL) {
|
||||
op = next->body.symbol;
|
||||
if (op == '(') {
|
||||
opstack[++sp] = '(';
|
||||
} else if (op == ')' || op == ',') {
|
||||
while (convertRpnPop(opstack[sp--]));
|
||||
if (op == ',') {
|
||||
sp++;
|
||||
}
|
||||
} else {
|
||||
prio = operatorPriority(op);
|
||||
if (isUnary(op)) {
|
||||
prio += 1;
|
||||
}
|
||||
while (sp >= 0 && operatorPriority(opstack[sp]) >= prio) {
|
||||
convertRpnPop(opstack[sp--]);
|
||||
}
|
||||
opstack[++sp] = op;
|
||||
}
|
||||
} else if (next->type == TT_FUNCTION) {
|
||||
opstack[++sp] = (char)(((char*)(void*)next) - start);
|
||||
opstack[++sp] = '(';
|
||||
} else if (next->type == TT_FUNC_END) {
|
||||
while (convertRpnPop(opstack[sp--]));
|
||||
copyToken(curTok, (token*)(void*)(start + opstack[sp--]));
|
||||
if (curTok->body.str.len == 1) {
|
||||
curTok->type = TT_ARRAY;
|
||||
curTok->body.symbol = curTok->body.str.text[0];
|
||||
}
|
||||
curTok = nextToken(curTok);
|
||||
}
|
||||
next = nextToken(next);
|
||||
}
|
||||
while (sp >= 0) {
|
||||
convertRpnPop(opstack[sp--]);
|
||||
}
|
||||
}
|
||||
|
||||
void convertToRpn(token* next) {
|
||||
char buf[MAX_LINE_LEN * 2];
|
||||
curTok->type = TT_ERROR;
|
||||
memcpy(buf, next, ((char*)(void*)curTok) - ((char*)(void*)next) + 1);
|
||||
curTok = next;
|
||||
next = (token*)(void*) buf;
|
||||
shuntingYard(next);
|
||||
curTok->type = TT_ERROR;
|
||||
prevTok = NULL;
|
||||
}
|
||||
|
||||
char parseExpression(void) {
|
||||
token* startTok = curTok;
|
||||
char funcBrackets[16];
|
||||
unsigned char iFuncBr = 0;
|
||||
char state = '1';
|
||||
funcBrackets[iFuncBr] = 0;
|
||||
if (*getCurTokPos() == 0) {
|
||||
setTokenError(getCurTokPos(), 10);
|
||||
return 0;
|
||||
}
|
||||
while (state != 's') {
|
||||
switch (state) {
|
||||
case '1':
|
||||
state = parseExprVal();
|
||||
break;
|
||||
case 'f':
|
||||
funcBrackets[++iFuncBr] = 1;
|
||||
state = '1';
|
||||
break;
|
||||
case '(':
|
||||
funcBrackets[++iFuncBr] = 0;
|
||||
state = '1';
|
||||
break;
|
||||
case '+':
|
||||
state = parseExprBop();
|
||||
break;
|
||||
case ')':
|
||||
state = parseExprRbr(iFuncBr, funcBrackets[iFuncBr]);
|
||||
iFuncBr -= 1;
|
||||
break;
|
||||
case ',':
|
||||
state = parseExprComma(funcBrackets[iFuncBr]++);
|
||||
break;
|
||||
case 'e':
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
convertToRpn(startTok);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
9
apps/tbasic/basic_expr.h
Normal file
9
apps/tbasic/basic_expr.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef __EXPR_H_
|
||||
#define __EXPR_H_
|
||||
|
||||
#include "basic_tokens.h"
|
||||
|
||||
char parseExpression(void);
|
||||
|
||||
#endif
|
||||
|
||||
26
apps/tbasic/basic_extern.h
Normal file
26
apps/tbasic/basic_extern.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef __EXTERN_H_
|
||||
#define __EXTERN_H_
|
||||
|
||||
extern char extraCmdArgCnt[];
|
||||
extern char extraFuncArgCnt[];
|
||||
|
||||
short extraCommandByHash(numeric h);
|
||||
short extraFunctionByHash(numeric h);
|
||||
extern char dataSpace[];
|
||||
extern char lineSpace[];
|
||||
extern char lastInput;
|
||||
|
||||
void sysPutc(char c);
|
||||
void sysEcho(char c);
|
||||
numeric sysMillis(numeric div);
|
||||
void extraCommand(char cmd, numeric args[]);
|
||||
numeric extraFunction(char cmd, numeric args[]);
|
||||
void outputConstStr(char strId, char index, char* s);
|
||||
|
||||
/* data=NULL for open/close, size > 0 for write,
|
||||
size < 0 for read, size=0 for close,
|
||||
abs(size) can be fileId */
|
||||
char storageOperation(void* data, short size);
|
||||
|
||||
#endif
|
||||
|
||||
284
apps/tbasic/basic_main.c
Normal file
284
apps/tbasic/basic_main.c
Normal file
@@ -0,0 +1,284 @@
|
||||
#include "mystdlib.h"
|
||||
#include "basic_tokens.h"
|
||||
#include "basic_editor.h"
|
||||
#include "basic_exectoks.h"
|
||||
#include "basic_utils.h"
|
||||
#include "basic_extern.h"
|
||||
#include "basic_textual.h"
|
||||
|
||||
static short listLine, listPage;
|
||||
token* toksBody;
|
||||
char mainState;
|
||||
|
||||
#if 0
|
||||
void printToken(token* t) {
|
||||
switch (t->type) {
|
||||
case TT_NUMBER:
|
||||
outputStr("INT=");
|
||||
outputInt(t->body.integer);
|
||||
break;
|
||||
case TT_NAME:
|
||||
outputStr("NAME=");
|
||||
outputNStr(&(t->body.str));
|
||||
break;
|
||||
case TT_VARIABLE:
|
||||
outputStr("VAR=");
|
||||
outputNStr(&(t->body.str));
|
||||
break;
|
||||
case TT_FUNCTION:
|
||||
outputStr("FN=");
|
||||
outputNStr(&(t->body.str));
|
||||
break;
|
||||
case TT_COMMAND:
|
||||
outputStr("CMD=");
|
||||
outputInt(t->body.command);
|
||||
break;
|
||||
case TT_LITERAL:
|
||||
outputStr("STR=\"");
|
||||
outputNStr(&(t->body.str));
|
||||
outputStr("\"");
|
||||
break;
|
||||
case TT_COMMENT:
|
||||
outputStr("REM=\"");
|
||||
outputNStr(&(t->body.str));
|
||||
outputStr("\"");
|
||||
break;
|
||||
case TT_SYMBOL:
|
||||
outputStr("SYM=");
|
||||
outputChar(t->body.symbol);
|
||||
break;
|
||||
case TT_ARRAY:
|
||||
outputStr("ARR=");
|
||||
outputChar(t->body.symbol);
|
||||
break;
|
||||
case TT_FUNC_END:
|
||||
outputStr("FE=%d");
|
||||
outputInt(t->body.symbol);
|
||||
break;
|
||||
case TT_NONE:
|
||||
outputChar('N');
|
||||
break;
|
||||
case TT_SEPARATOR:
|
||||
outputChar(';');
|
||||
break;
|
||||
default:
|
||||
outputChar('E');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void printTokens(token* t) {
|
||||
while (1) {
|
||||
printToken(t);
|
||||
outputChar(' ');
|
||||
if (tokenClass(t) == TT_NONE) {
|
||||
break;
|
||||
}
|
||||
t = nextToken(t);
|
||||
}
|
||||
outputCr();
|
||||
}
|
||||
#else
|
||||
void printTokens(token* t) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void printProgram(void) {
|
||||
prgline* p = findLine(listLine);
|
||||
if (p->num == 0 && listLine > 1) {
|
||||
p = findLine(1);
|
||||
}
|
||||
short lineCount = 0;
|
||||
while (p->num != 0 && lineCount < listPage) {
|
||||
listLine = p->num + 1;
|
||||
outputInt(p->num);
|
||||
outputChar(' ');
|
||||
outputNStr(&(p->str));
|
||||
outputCr();
|
||||
p = findLine(p->num + 1);
|
||||
lineCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void listProgram(token* t) {
|
||||
t = nextToken(nextToken(t));
|
||||
if (t->type == TT_NUMBER) {
|
||||
listLine = t->body.integer;
|
||||
t = nextToken(t);
|
||||
if (t->type == TT_NUMBER) {
|
||||
listPage = t->body.integer;
|
||||
}
|
||||
}
|
||||
printProgram();
|
||||
}
|
||||
|
||||
void executeSteps() {
|
||||
token* t = nextToken(nextToken(toksBody));
|
||||
mainState |= STATE_STEPS;
|
||||
executeNonParsed(t->type == TT_NUMBER ? t->body.integer : 1);
|
||||
}
|
||||
|
||||
void executeRun() {
|
||||
if (editorSave()) {
|
||||
editorLoadParsed();
|
||||
initParsedRun();
|
||||
} else {
|
||||
executeNonParsed(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void manualSave(void) {
|
||||
editorSave();
|
||||
outputConstStr(ID_COMMON_STRINGS, 6, NULL); // Saved
|
||||
outputChar(' ');
|
||||
outputInt(prgSize > 2 ? prgSize + 2 : 0);
|
||||
outputChar(' ');
|
||||
outputConstStr(ID_COMMON_STRINGS, 8, NULL); // bytes
|
||||
outputCr();
|
||||
}
|
||||
|
||||
void manualLoad(void) {
|
||||
if (editorLoad()) {
|
||||
outputConstStr(ID_COMMON_STRINGS, 7, NULL); // Loaded
|
||||
outputChar(' ');
|
||||
outputInt(prgSize + 2);
|
||||
outputChar(' ');
|
||||
outputConstStr(ID_COMMON_STRINGS, 8, NULL); // bytes
|
||||
outputCr();
|
||||
} else {
|
||||
outputConstStr(ID_COMMON_STRINGS, 9, NULL); // bytes
|
||||
outputCr();
|
||||
}
|
||||
}
|
||||
|
||||
void prgReset(void) {
|
||||
resetEditor();
|
||||
resetTokenExecutor();
|
||||
}
|
||||
|
||||
void showInfo(void) {
|
||||
outputConstStr(ID_COMMON_STRINGS, 1, NULL); // code:
|
||||
outputInt(prgSize);
|
||||
outputCr();
|
||||
outputConstStr(ID_COMMON_STRINGS, 2, NULL); // vars:
|
||||
outputInt(varSize());
|
||||
outputCr();
|
||||
outputConstStr(ID_COMMON_STRINGS, 3, NULL); // next:
|
||||
outputInt(nextLineNum);
|
||||
outputCr();
|
||||
}
|
||||
|
||||
void metaOrError() {
|
||||
numeric h = tokenHash(toksBody);
|
||||
if (h == 0x3B6) { // LIST
|
||||
listProgram(toksBody);
|
||||
} else if (h == 0x312) { // STEP
|
||||
executeSteps();
|
||||
} else if (h == 0x1AC) { // RUN
|
||||
executeRun();
|
||||
} else if (h == 0x375) { // SAVE
|
||||
manualSave();
|
||||
} else if (h == 0x39A) { // LOAD
|
||||
manualLoad();
|
||||
} else if (h == 0x69A) { // RESET
|
||||
prgReset();
|
||||
} else if (h == 0x3B3) { // INFO
|
||||
showInfo();
|
||||
} else {
|
||||
getParseErrorMsg(lineSpace);
|
||||
outputStr(lineSpace);
|
||||
outputChar(' ');
|
||||
outputInt((long)(getParseErrorPos() - lineSpace) + 1);
|
||||
outputCr();
|
||||
}
|
||||
}
|
||||
|
||||
void processLine() {
|
||||
if (lineSpace[0] == 0) {
|
||||
return;
|
||||
}
|
||||
parseLine(lineSpace, toksBody);
|
||||
printTokens(toksBody);
|
||||
if (getParseErrorPos() != NULL) {
|
||||
metaOrError();
|
||||
return;
|
||||
}
|
||||
if (toksBody->type != TT_NUMBER) {
|
||||
executeTokens(toksBody);
|
||||
} else {
|
||||
injectLine(skipSpaces(skipDigits(lineSpace)), toksBody->body.integer);
|
||||
}
|
||||
}
|
||||
|
||||
void preload(char* line, token* t) {
|
||||
if (editorLoadParsed()) {
|
||||
outputConstStr(ID_COMMON_STRINGS, 10, NULL); // code found, autorun message
|
||||
outputCr();
|
||||
setDelay(1000);
|
||||
mainState = STATE_PRELOAD;
|
||||
} else {
|
||||
prgReset();
|
||||
}
|
||||
}
|
||||
|
||||
void init(short dataSize, short lineSize, short progSize) {
|
||||
outputCr();
|
||||
outputConstStr(ID_COMMON_STRINGS, 0, NULL); // TBASIC vX.X
|
||||
outputCr();
|
||||
initEditor(dataSpace + dataSize, progSize);
|
||||
initTokenExecutor(dataSpace, dataSize);
|
||||
listLine = 1;
|
||||
listPage = 10;
|
||||
mainState = STATE_INTERACTIVE;
|
||||
toksBody = (token*)(void*) (lineSpace + lineSize);
|
||||
preload(lineSpace, toksBody);
|
||||
}
|
||||
|
||||
void waitPreloadRunDelay() {
|
||||
if (lastInput > 0) {
|
||||
mainState &= ~STATE_PRELOAD;
|
||||
outputConstStr(ID_COMMON_STRINGS, 11, NULL); // canceled
|
||||
outputCr();
|
||||
editorLoad();
|
||||
} else if (checkDelay()) {
|
||||
mainState &= ~STATE_PRELOAD;
|
||||
initParsedRun();
|
||||
}
|
||||
}
|
||||
|
||||
void dispatch() {
|
||||
if (lastInput == 3) {
|
||||
mainState |= STATE_BREAK;
|
||||
}
|
||||
if ((mainState & (STATE_RUN | STATE_SLOWED)) == STATE_RUN) {
|
||||
executeParsedRun();
|
||||
return;
|
||||
}
|
||||
switch (mainState & STATE_SLOWED) {
|
||||
case STATE_DELAY:
|
||||
dispatchDelay();
|
||||
return;
|
||||
case STATE_INPUT:
|
||||
dispatchInput();
|
||||
lastInput = 0;
|
||||
return;
|
||||
case STATE_BREAK:
|
||||
dispatchBreak();
|
||||
lastInput = 0;
|
||||
return;
|
||||
}
|
||||
if ((mainState & STATE_STEPS) != 0) {
|
||||
executeNonParsed(0);
|
||||
} else if ((mainState & STATE_PRELOAD) != 0) {
|
||||
waitPreloadRunDelay();
|
||||
} else {
|
||||
if (lastInput > 0) {
|
||||
if (readLine()) {
|
||||
processLine();
|
||||
}
|
||||
lastInput = 0;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
11
apps/tbasic/basic_main.h
Normal file
11
apps/tbasic/basic_main.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef __MAIN_H_
|
||||
#define __MAIN_H_
|
||||
|
||||
#include "mytypes.h"
|
||||
|
||||
void init(short dataSize, short lineSize, short progSize);
|
||||
void dispatch();
|
||||
void processLine(char* line, token* t);
|
||||
|
||||
#endif
|
||||
|
||||
16
apps/tbasic/basic_textual.h
Normal file
16
apps/tbasic/basic_textual.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef __TEXTUAL_H_
|
||||
#define __TEXTUAL_H_
|
||||
|
||||
#define CONST_COMMON_STRINGS "TBASIC 1.7\ncode: \nvars: \nnext: \n"\
|
||||
"BREAK\nEnd of code\nSaved\nLoaded\nbytes\nLoad failed\n"\
|
||||
"Autorun in 1 sec\nCanceled!\nLow VARS mem\nLow PROG mem\n"
|
||||
|
||||
#define CONST_PARSING_ERRORS "\nCmd or Var expectedd\nSymbol '=' expected\nName expected\n"\
|
||||
"Symbol ';' expected\nExtra chars at end\nUnexpected error\nNumber out of range\n"\
|
||||
"Number expected\nUnexpected symbol\nUnexpected line end\n"
|
||||
|
||||
#define ID_COMMON_STRINGS 0
|
||||
#define ID_PARSING_ERRORS 1
|
||||
|
||||
#endif
|
||||
|
||||
15
apps/tbasic/basic_tokenint.h
Normal file
15
apps/tbasic/basic_tokenint.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef __TOKENINT_H_
|
||||
#define __TOKENINT_H_
|
||||
|
||||
extern token* curTok;
|
||||
extern token* prevTok;
|
||||
|
||||
char* getCurTokPos();
|
||||
void skipTokenInInput(char offset);
|
||||
char parseName(char checkCmd);
|
||||
char parseSymbol(void);
|
||||
char parseNumber(void);
|
||||
char parseNone();
|
||||
void setTokenError(char* pos, char code);
|
||||
|
||||
#endif
|
||||
510
apps/tbasic/basic_tokens.c
Normal file
510
apps/tbasic/basic_tokens.c
Normal file
@@ -0,0 +1,510 @@
|
||||
#include "mystdlib.h"
|
||||
#include "basic_tokens.h"
|
||||
#include "basic_expr.h"
|
||||
#include "basic_utils.h"
|
||||
#include "basic_extern.h"
|
||||
#include "basic_textual.h"
|
||||
|
||||
char* cur;
|
||||
token* curTok;
|
||||
token* prevTok;
|
||||
char* parseError;
|
||||
char parseErrorCode;
|
||||
|
||||
static short cmdCodeByHash(numeric h) {
|
||||
// replaced array with switch to save RAM
|
||||
switch (h) {
|
||||
case 0x018F: // REM
|
||||
return CMD_REM;
|
||||
case 0x067C: // PRINT
|
||||
return CMD_PRINT;
|
||||
case 0x075E: // INPUT
|
||||
return CMD_INPUT;
|
||||
case 0x00D4: // IF
|
||||
return CMD_IF;
|
||||
case 0x03E3: // GOTO
|
||||
return CMD_GOTO;
|
||||
case 0x07AC: // GOSUB
|
||||
return CMD_GOSUB;
|
||||
case 0x0D0E: // RETURN
|
||||
return CMD_RETURN;
|
||||
case 0x01CC: // END
|
||||
return CMD_END;
|
||||
case 0x01CF: // DIM
|
||||
return CMD_DIM;
|
||||
case 0x0783: // DELAY
|
||||
return CMD_DELAY;
|
||||
case 0x03CD: // DATA
|
||||
return CMD_DATA;
|
||||
case 0x03DA: // EMIT
|
||||
return CMD_EMIT;
|
||||
default:
|
||||
return extraCommandByHash(h);
|
||||
}
|
||||
}
|
||||
|
||||
char* getCurTokPos() {
|
||||
return cur;
|
||||
}
|
||||
|
||||
short tokenSize(token* t) {
|
||||
switch (t->type) {
|
||||
case TT_NUMBER:
|
||||
return 1 + sizeof(t->body.integer);
|
||||
case TT_NAME:
|
||||
case TT_COMMENT:
|
||||
case TT_LITERAL:
|
||||
case TT_FUNCTION:
|
||||
case TT_VARIABLE:
|
||||
return 2 + t->body.str.len;
|
||||
case TT_FUNC_END:
|
||||
case TT_SYMBOL:
|
||||
case TT_ARRAY:
|
||||
case TT_COMMAND:
|
||||
return 1 + sizeof(t->body.symbol);
|
||||
case TT_NONE:
|
||||
case TT_ERROR:
|
||||
case TT_SEPARATOR:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
short tokenChainSize(token* src) {
|
||||
token* t = src;
|
||||
while (t->type != TT_NONE) {
|
||||
t = nextToken(t);
|
||||
}
|
||||
return (short)((char*)(void*)t - (char*)(void*)src) + 1;
|
||||
}
|
||||
|
||||
void copyToken(token* dst, token* src) {
|
||||
memcpy(dst, src, tokenSize(src));
|
||||
}
|
||||
|
||||
void setTokenError(char* pos, char code) {
|
||||
parseErrorCode = code;
|
||||
parseError = pos;
|
||||
curTok->type = TT_ERROR;
|
||||
}
|
||||
|
||||
token* nextToken(token* t) {
|
||||
return (token*)(void*)((char*)(void*)t + tokenSize(t));
|
||||
}
|
||||
|
||||
void skipTokenInInput(char offset) {
|
||||
cur = skipSpaces(cur + offset);
|
||||
}
|
||||
|
||||
static void advance(char* s) {
|
||||
cur = skipSpaces(s);
|
||||
prevTok = curTok;
|
||||
curTok = nextToken(curTok);
|
||||
}
|
||||
|
||||
static void substCommandFound(char code) {
|
||||
curTok->type = TT_COMMAND;
|
||||
curTok->body.command = code;
|
||||
nextToken(curTok)->type = TT_ERROR;
|
||||
}
|
||||
|
||||
void trySubstCmd(void) {
|
||||
short i;
|
||||
numeric cmdHash = tokenHash(curTok);
|
||||
i = cmdCodeByHash(cmdHash);
|
||||
if (i >= 0) {
|
||||
substCommandFound(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char parseName(char checkCmd) {
|
||||
short i = 0;
|
||||
if (!isAlpha(*cur)) {
|
||||
return 0;
|
||||
}
|
||||
curTok->type = TT_NAME;
|
||||
while (isAlNum(cur[i])) {
|
||||
curTok->body.str.text[i] = toUpper(cur[i]);
|
||||
i++;
|
||||
}
|
||||
curTok->body.str.len = i;
|
||||
if (checkCmd) {
|
||||
trySubstCmd();
|
||||
}
|
||||
advance(cur + i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char parseChar(void) {
|
||||
if (cur[0] != '\'') {
|
||||
return 0;
|
||||
}
|
||||
curTok->type = TT_NUMBER;
|
||||
curTok->body.integer = ((unsigned char*) cur)[1];
|
||||
cur += 2;
|
||||
advance(cur);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char parseNumber(void) {
|
||||
char base = 10;
|
||||
if (!isDigit(*cur)) {
|
||||
return parseChar();
|
||||
}
|
||||
curTok->type = TT_NUMBER;
|
||||
curTok->body.integer = 0;
|
||||
if (cur[0] == '0') {
|
||||
if (toUpper(cur[1]) == 'X') {
|
||||
base = 16;
|
||||
cur += 2;
|
||||
} else if (toUpper(cur[1]) == 'B') {
|
||||
base = 2;
|
||||
cur += 2;
|
||||
} else {
|
||||
base = 8;
|
||||
}
|
||||
}
|
||||
while (isDigitBased(*cur, base)) {
|
||||
curTok->body.integer = curTok->body.integer * base + makeDigit(*cur, base);
|
||||
cur++;
|
||||
}
|
||||
advance(cur);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char parseNone(void) {
|
||||
if (*cur != 0) {
|
||||
setTokenError(cur, 5);
|
||||
return 0;
|
||||
}
|
||||
curTok->type = TT_NONE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
char parseComment(void) {
|
||||
unsigned char len = strlen(cur);
|
||||
curTok->type = TT_COMMENT;
|
||||
curTok->body.str.len = len;
|
||||
memcpy(&(curTok->body.str.text), cur, len);
|
||||
advance(cur + len);
|
||||
return parseNone();
|
||||
}
|
||||
|
||||
char parseLiteral() {
|
||||
if (*cur != '"') {
|
||||
return 0;
|
||||
}
|
||||
short i = 1;
|
||||
curTok->type = TT_LITERAL;
|
||||
while (cur[i] != 0 && cur[i] != '"') {
|
||||
curTok->body.str.text[i - 1] = cur[i];
|
||||
i++;
|
||||
}
|
||||
curTok->body.str.len = i - 1;
|
||||
advance(cur + i + (cur[i] == '"' ? 1 : 0));
|
||||
return 1;
|
||||
}
|
||||
|
||||
char parseSymbol() {
|
||||
curTok->type = TT_SYMBOL;
|
||||
char c = 0;
|
||||
if (cur[0] == '<') {
|
||||
if (cur[1] == '>') {
|
||||
c = '#';
|
||||
} else if (cur[1] == '=') {
|
||||
c = '{';
|
||||
}
|
||||
} else if (cur[0] == '>' && cur[1] == '=') {
|
||||
c = '}';
|
||||
}
|
||||
if (c != 0) {
|
||||
cur++;
|
||||
} else {
|
||||
c = cur[0];
|
||||
}
|
||||
curTok->body.symbol = toUpper(c);
|
||||
advance(cur + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char parseLineNumber(void) {
|
||||
char* start = cur;
|
||||
if (!parseNumber()) {
|
||||
return 1;
|
||||
}
|
||||
if (prevTok->body.integer < 1 || prevTok->body.integer > MAX_LINE_NUMBER) {
|
||||
setTokenError(start, 7);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
char parseSemicolon(void) {
|
||||
if (*cur != ';') {
|
||||
setTokenError(cur, 4);
|
||||
return 0;
|
||||
}
|
||||
curTok->type = TT_SEPARATOR;
|
||||
advance(cur + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char* skipSubscripts() {
|
||||
char* p = cur;
|
||||
char br = 0;
|
||||
while (*p != 0) {
|
||||
if (*p == '(') {
|
||||
br += 1;
|
||||
} else if (*p == ')') {
|
||||
br -= 1;
|
||||
if (br == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
char assignmentSyntax(void) {
|
||||
char* p;
|
||||
if (*cur == '=') {
|
||||
return CMD_LET;
|
||||
} else if (*cur != '(') {
|
||||
setTokenError(cur, 2);
|
||||
return 0;
|
||||
}
|
||||
p = skipSubscripts();
|
||||
if (*p == 0) {
|
||||
setTokenError(p, 10);
|
||||
return 0;
|
||||
}
|
||||
p = skipSpaces(p + 1);
|
||||
if (*p != '=') {
|
||||
setTokenError(cur, 2);
|
||||
return 0;
|
||||
}
|
||||
return CMD_LETA;
|
||||
}
|
||||
|
||||
char parseSubscripts(void) {
|
||||
char* p = skipSubscripts();
|
||||
*p = ';';
|
||||
cur++;
|
||||
if (!parseExpression()) {
|
||||
return 0;
|
||||
}
|
||||
parseSemicolon();
|
||||
*p = ')';
|
||||
return 1;
|
||||
}
|
||||
|
||||
char parseAssignment(void) {
|
||||
char synt = assignmentSyntax();
|
||||
if (!synt) {
|
||||
return 0;
|
||||
}
|
||||
curTok->type = TT_COMMAND;
|
||||
memmove((char*)(void*)prevTok + tokenSize(curTok), prevTok, tokenSize(prevTok));
|
||||
prevTok->type = TT_COMMAND;
|
||||
prevTok->body.command = synt;
|
||||
curTok = nextToken(prevTok);
|
||||
if (synt == CMD_LETA) {
|
||||
curTok->type = TT_ARRAY;
|
||||
curTok->body.symbol = curTok->body.str.text[0];
|
||||
}
|
||||
prevTok = curTok;
|
||||
curTok = nextToken(curTok);
|
||||
if (synt == CMD_LETA) {
|
||||
if (!parseSubscripts()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
cur = skipSpaces(cur + 1);
|
||||
return parseExpression() && parseNone();
|
||||
}
|
||||
|
||||
char parseExprOrLiteral(void) {
|
||||
if (parseLiteral()) {
|
||||
return 1;
|
||||
}
|
||||
return parseExpression();
|
||||
}
|
||||
|
||||
char parseVar(void) {
|
||||
if (!parseName(0)) {
|
||||
setTokenError(cur, 3);
|
||||
return 0;
|
||||
}
|
||||
prevTok->type = TT_VARIABLE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
char parseExprList(void) {
|
||||
if (!parseExpression()) {
|
||||
return 0;
|
||||
}
|
||||
while (*cur != 0) {
|
||||
if (!parseSemicolon() || !parseExpression()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return parseNone();
|
||||
}
|
||||
|
||||
char parsePrintList(void) {
|
||||
if (!parseExprOrLiteral()) {
|
||||
return 0;
|
||||
}
|
||||
while (*cur != 0) {
|
||||
if (!parseSemicolon() || !parseExprOrLiteral()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return parseNone();
|
||||
}
|
||||
|
||||
char parseDataList(void) {
|
||||
do {
|
||||
if (!parseNumber() && !parseLiteral()) {
|
||||
setTokenError(cur, 8);
|
||||
return 0;
|
||||
}
|
||||
} while (*cur != 0);
|
||||
return parseNone();
|
||||
}
|
||||
|
||||
char parseNExpressions(char cnt) {
|
||||
if (cnt > 0) {
|
||||
if (!parseExpression()) {
|
||||
return 0;
|
||||
}
|
||||
while (--cnt > 0) {
|
||||
if (!parseSemicolon() || !parseExpression()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return parseNone();
|
||||
}
|
||||
|
||||
char parseLabel(void) {
|
||||
if (parseNumber()) {
|
||||
return parseNone();
|
||||
}
|
||||
setTokenError(cur, 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char parseStatement(void);
|
||||
|
||||
char parseConditional(void) {
|
||||
if (!parseExpression()) {
|
||||
return 0;
|
||||
}
|
||||
return parseSemicolon() && parseStatement();
|
||||
}
|
||||
|
||||
char parseAllocate() {
|
||||
if (!parseName(0)) {
|
||||
setTokenError(cur, 3);
|
||||
return 0;
|
||||
}
|
||||
prevTok->type = TT_ARRAY;
|
||||
prevTok->body.symbol = prevTok->body.str.text[0];
|
||||
curTok = nextToken(prevTok);
|
||||
if (!parseNumber()) {
|
||||
setTokenError(cur, 8);
|
||||
return 0;
|
||||
}
|
||||
if (*cur != 0) {
|
||||
if (!parseSymbol() || prevTok->body.symbol != 'B') {
|
||||
setTokenError(cur, 9);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return parseNone();
|
||||
}
|
||||
|
||||
void parseSpecialWithError() {
|
||||
curTok = nextToken(curTok);
|
||||
while (parseName(0) || parseNumber() || parseLiteral()) {
|
||||
}
|
||||
curTok->type = TT_ERROR;
|
||||
}
|
||||
|
||||
char parseStatement(void) {
|
||||
char cmd;
|
||||
if (!parseName(1)) {
|
||||
setTokenError(cur, 1);
|
||||
return 0;
|
||||
} else if (prevTok->type != TT_COMMAND) {
|
||||
if (parseAssignment()) {
|
||||
return 1;
|
||||
} else {
|
||||
parseSpecialWithError();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
cmd = prevTok->body.command;
|
||||
if (cmd == CMD_REM) {
|
||||
return parseComment();
|
||||
} else if (cmd == CMD_GOTO || cmd == CMD_GOSUB) {
|
||||
return parseLabel();
|
||||
} else if (cmd == CMD_RETURN || cmd == CMD_END) {
|
||||
return parseNone();
|
||||
} else if (cmd == CMD_PRINT) {
|
||||
return parsePrintList();
|
||||
} else if (cmd == CMD_INPUT) {
|
||||
return parseVar() && parseNone();
|
||||
} else if (cmd == CMD_IF) {
|
||||
return parseConditional();
|
||||
} else if (cmd == CMD_DIM) {
|
||||
return parseAllocate();
|
||||
} else if (cmd == CMD_DELAY) {
|
||||
return parseNExpressions(1);
|
||||
} else if (cmd == CMD_DATA) {
|
||||
return parseDataList();
|
||||
} else if (cmd == CMD_EMIT) {
|
||||
return parseExprList();
|
||||
} else if (cmd >= CMD_EXTRA) {
|
||||
return parseNExpressions(extraCmdArgCnt[cmd - CMD_EXTRA]);
|
||||
}
|
||||
setTokenError(cur, 6);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void parseLine(char* line, token* tokens) {
|
||||
cur = line;
|
||||
curTok = tokens;
|
||||
prevTok = NULL;
|
||||
setTokenError(NULL, 0);
|
||||
if (parseLineNumber()) {
|
||||
if (*cur != 0) {
|
||||
parseStatement();
|
||||
} else {
|
||||
parseNone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char tokenClass(token* t) {
|
||||
return t->type & 0xF0;
|
||||
}
|
||||
|
||||
numeric tokenHash(token* t) {
|
||||
if (tokenClass(t) != TT_NAME) {
|
||||
return 0;
|
||||
}
|
||||
return hashOfNStr(&(t->body.str));
|
||||
}
|
||||
|
||||
char* getParseErrorPos(void) {
|
||||
return parseError;
|
||||
}
|
||||
|
||||
void getParseErrorMsg(char* s) {
|
||||
outputConstStr(ID_PARSING_ERRORS, parseErrorCode, s);
|
||||
}
|
||||
|
||||
49
apps/tbasic/basic_tokens.h
Normal file
49
apps/tbasic/basic_tokens.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef __TOKENS_H_
|
||||
#define __TOKENS_H_
|
||||
|
||||
#define TT_NONE 0x00
|
||||
#define TT_ERROR 0x01
|
||||
#define TT_NUMBER 0x10
|
||||
#define TT_NAME 0x20
|
||||
#define TT_COMMAND 0x21
|
||||
#define TT_VARIABLE 0x22
|
||||
#define TT_FUNCTION 0x23
|
||||
#define TT_SYMBOL 0x30
|
||||
#define TT_FUNC_END 0x31
|
||||
#define TT_SEPARATOR 0x32
|
||||
#define TT_ARRAY 0x33
|
||||
#define TT_LITERAL 0x40
|
||||
#define TT_COMMENT 0x41
|
||||
|
||||
#define CMD_REM 0
|
||||
#define CMD_PRINT 1
|
||||
#define CMD_INPUT 2
|
||||
#define CMD_IF 3
|
||||
#define CMD_GOTO 4
|
||||
#define CMD_GOSUB 5
|
||||
#define CMD_RETURN 6
|
||||
#define CMD_END 7
|
||||
#define CMD_LET 8
|
||||
#define CMD_LETA 9
|
||||
#define CMD_DIM 10
|
||||
#define CMD_DELAY 11
|
||||
#define CMD_DATA 12
|
||||
#define CMD_EMIT 13
|
||||
#define CMD_EXTRA 0x40
|
||||
|
||||
#define MAX_LINE_NUMBER 30000
|
||||
|
||||
#include "basic_utils.h"
|
||||
|
||||
void parseLine(char* line, token* tokens);
|
||||
short tokenSize(token* t);
|
||||
short tokenChainSize(token* t);
|
||||
token* nextToken(token* t);
|
||||
void copyToken(token* dst, token* src);
|
||||
char tokenClass(token* t);
|
||||
numeric tokenHash(token* t);
|
||||
char* getParseErrorPos(void);
|
||||
void getParseErrorMsg(char*);
|
||||
|
||||
#endif
|
||||
|
||||
183
apps/tbasic/basic_utils.c
Normal file
183
apps/tbasic/basic_utils.c
Normal file
@@ -0,0 +1,183 @@
|
||||
#include "mystdlib.h"
|
||||
#include "basic_utils.h"
|
||||
#include "basic_extern.h"
|
||||
|
||||
void trim(char* s) {
|
||||
short i = 0, k;
|
||||
while (s[i] != 0 && s[i] <= ' ') {
|
||||
i++;
|
||||
}
|
||||
k = 0;
|
||||
while (s[i] != 0) {
|
||||
s[k++] = s[i++];
|
||||
}
|
||||
do {
|
||||
s[k--] = 0;
|
||||
} while (k >= 0 && s[k] <= ' ');
|
||||
}
|
||||
|
||||
char* skipSpaces(char* s) {
|
||||
while (isSpace(*s)) {
|
||||
s++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
char* skipDigits(char* s) {
|
||||
while (isDigit(*s)) {
|
||||
s++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
char charInStr(char c, char* s) {
|
||||
while (*s != 0) {
|
||||
if (*s == c) {
|
||||
return 1;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char cmpNStrToStr(nstring* ns, char* s) {
|
||||
if (ns->len != strlen(s)) {
|
||||
return 0;
|
||||
}
|
||||
return memcmp(&(ns->text), s, ns->len) == 0;
|
||||
}
|
||||
|
||||
void outputChar(char c) {
|
||||
sysPutc(c);
|
||||
}
|
||||
|
||||
void outputStr(char* s) {
|
||||
while (*s) {
|
||||
sysPutc(*(s++));
|
||||
}
|
||||
}
|
||||
|
||||
void outputNStr(nstring* t) {
|
||||
short i;
|
||||
for (i = 0; i < t->len; i++) {
|
||||
sysPutc(t->text[i]);
|
||||
}
|
||||
};
|
||||
|
||||
void outputInt(long n) {
|
||||
long d = 1000000000;
|
||||
if (n < 0) {
|
||||
outputChar('-');
|
||||
n = -n;
|
||||
}
|
||||
if (n == 0) {
|
||||
sysPutc('0');
|
||||
return;
|
||||
}
|
||||
while (d > n) {
|
||||
d /= 10;
|
||||
}
|
||||
while (d > 0) {
|
||||
sysPutc(n / d + '0');
|
||||
n %= d;
|
||||
d /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
int decFromStr(char* s) {
|
||||
schar sign = 1;
|
||||
char base = 10;
|
||||
int res = 0;
|
||||
if (*s == '-') {
|
||||
sign = -1;
|
||||
s += 1;
|
||||
}
|
||||
if (*s == '0') {
|
||||
s += 1;
|
||||
if (toUpper(*s) == 'X') {
|
||||
base = 16;
|
||||
s += 1;
|
||||
} else if (toUpper(*s) == 'B') {
|
||||
base = 2;
|
||||
s += 1;
|
||||
} else {
|
||||
base = 8;
|
||||
}
|
||||
}
|
||||
while (isDigitBased(*s, base)) {
|
||||
res = res * base + makeDigit(*s++, base);
|
||||
}
|
||||
return res * sign;
|
||||
}
|
||||
|
||||
void outputCr() {
|
||||
sysPutc('\n');
|
||||
}
|
||||
|
||||
char toUpper(char c) {
|
||||
return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c;
|
||||
}
|
||||
|
||||
char isAlpha(char c) {
|
||||
c = toUpper(c);
|
||||
return c >= 'A' && c <= 'Z';
|
||||
}
|
||||
|
||||
char isDigit(char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
char isDigitBased(char c, char base) {
|
||||
if (c < '0') {
|
||||
return 0;
|
||||
} else if (c <= '9') {
|
||||
return base > c - '0';
|
||||
} else {
|
||||
c = toUpper(c);
|
||||
return c >= 'A' && base > c - 'A' + 10;
|
||||
}
|
||||
}
|
||||
|
||||
char makeDigit(char c, char base) {
|
||||
if (c < '0') {
|
||||
return 0;
|
||||
} else if (c <= '9') {
|
||||
return c - '0';
|
||||
} else {
|
||||
return toUpper(c) - 'A' + 10;
|
||||
}
|
||||
}
|
||||
|
||||
char isAlNum(char c) {
|
||||
return isDigit(c) || isAlpha(c);
|
||||
}
|
||||
|
||||
char isSpace(char c) {
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\f':
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
numeric hashOfNStr(nstring* t) {
|
||||
unsigned char i = 0, n = t->len;
|
||||
numeric res = 0;
|
||||
while (i < n) {
|
||||
res = (res << 1) ^ t->text[i];
|
||||
i++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void pause(numeric millis) {
|
||||
numeric t0 = sysMillis(1);
|
||||
while (sysMillis(1) - t0 < millis) {
|
||||
}
|
||||
}
|
||||
|
||||
29
apps/tbasic/basic_utils.h
Normal file
29
apps/tbasic/basic_utils.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef __UTILS_H_
|
||||
#define __UTILS_H_
|
||||
|
||||
#include "mytypes.h"
|
||||
|
||||
void trim(char* s);
|
||||
char* skipSpaces(char* s);
|
||||
char* skipDigits(char* s);
|
||||
char charInStr(char c, char* s);
|
||||
char cmpNStrToStr(nstring* ns, char* s);
|
||||
int decFromStr(char* s);
|
||||
char isDigit(char c);
|
||||
char isDigitBased(char c, char base);
|
||||
char isAlpha(char c);
|
||||
char isAlNum(char c);
|
||||
char isSpace(char c);
|
||||
char toUpper(char c);
|
||||
char makeDigit(char c, char base);
|
||||
numeric hashOfNStr(nstring* t);
|
||||
void pause(numeric millis);
|
||||
|
||||
void outputChar(char c);
|
||||
void outputStr(char* s);
|
||||
void outputNStr(nstring* s);
|
||||
void outputInt(long n);
|
||||
void outputCr();
|
||||
|
||||
#endif
|
||||
|
||||
1
apps/tbasic/build.sh
Executable file
1
apps/tbasic/build.sh
Executable file
@@ -0,0 +1 @@
|
||||
g++ -Wall -Wno-write-strings -Wno-char-subscripts -o tinybas.out main_nix.c ../core/*.c
|
||||
97
apps/tbasic/ctype.h
Normal file
97
apps/tbasic/ctype.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#ifndef _CTYPE_H_
|
||||
#define _CTYPE_H_
|
||||
#include "_ansi.h"
|
||||
_BEGIN_STD_C
|
||||
int _EXFUN(isalnum, (int __c));
|
||||
int _EXFUN(isalpha, (int __c));
|
||||
int _EXFUN(iscntrl, (int __c));
|
||||
int _EXFUN(isdigit, (int __c));
|
||||
int _EXFUN(isgraph, (int __c));
|
||||
int _EXFUN(islower, (int __c));
|
||||
int _EXFUN(isprint, (int __c));
|
||||
int _EXFUN(ispunct, (int __c));
|
||||
int _EXFUN(isspace, (int __c));
|
||||
int _EXFUN(isupper, (int __c));
|
||||
int _EXFUN(isxdigit,(int __c));
|
||||
int _EXFUN(tolower, (int __c));
|
||||
int _EXFUN(toupper, (int __c));
|
||||
#if !defined(__STRICT_ANSI__) || defined(__cplusplus) || __STDC_VERSION__ >= 199901L
|
||||
int _EXFUN(isblank, (int __c));
|
||||
#endif
|
||||
#ifndef __STRICT_ANSI__
|
||||
int _EXFUN(isascii, (int __c));
|
||||
int _EXFUN(toascii, (int __c));
|
||||
#define _tolower(__c) ((unsigned char)(__c) - 'A' + 'a')
|
||||
#define _toupper(__c) ((unsigned char)(__c) - 'a' + 'A')
|
||||
#endif
|
||||
#define _U 01
|
||||
#define _L 02
|
||||
#define _N 04
|
||||
#define _S 010
|
||||
#define _P 020
|
||||
#define _C 040
|
||||
#define _X 0100
|
||||
#define _B 0200
|
||||
#ifndef _MB_CAPABLE
|
||||
_CONST
|
||||
#endif
|
||||
extern __IMPORT char *__ctype_ptr__;
|
||||
#ifndef __cplusplus
|
||||
/* These macros are intentionally written in a manner that will trigger
|
||||
a gcc -Wall warning if the user mistakenly passes a 'char' instead
|
||||
of an int containing an 'unsigned char'. Note that the sizeof will
|
||||
always be 1, which is what we want for mapping EOF to __ctype_ptr__[0];
|
||||
the use of a raw index inside the sizeof triggers the gcc warning if
|
||||
__c was of type char, and sizeof masks side effects of the extra __c.
|
||||
Meanwhile, the real index to __ctype_ptr__+1 must be cast to int,
|
||||
since isalpha(0x100000001LL) must equal isalpha(1), rather than being
|
||||
an out-of-bounds reference on a 64-bit machine. */
|
||||
#define __ctype_lookup(__c) ((__ctype_ptr__+sizeof(""[__c]))[(int)(__c)])
|
||||
#define isalpha(__c) (__ctype_lookup(__c)&(_U|_L))
|
||||
#define isupper(__c) ((__ctype_lookup(__c)&(_U|_L))==_U)
|
||||
#define islower(__c) ((__ctype_lookup(__c)&(_U|_L))==_L)
|
||||
#define isdigit(__c) (__ctype_lookup(__c)&_N)
|
||||
#define isxdigit(__c) (__ctype_lookup(__c)&(_X|_N))
|
||||
#define isspace(__c) (__ctype_lookup(__c)&_S)
|
||||
#define ispunct(__c) (__ctype_lookup(__c)&_P)
|
||||
#define isalnum(__c) (__ctype_lookup(__c)&(_U|_L|_N))
|
||||
#define isprint(__c) (__ctype_lookup(__c)&(_P|_U|_L|_N|_B))
|
||||
#define isgraph(__c) (__ctype_lookup(__c)&(_P|_U|_L|_N))
|
||||
#define iscntrl(__c) (__ctype_lookup(__c)&_C)
|
||||
#if defined(__GNUC__) && \
|
||||
(!defined(__STRICT_ANSI__) || __STDC_VERSION__ >= 199901L)
|
||||
#define isblank(__c) \
|
||||
__extension__ ({ __typeof__ (__c) __x = (__c); \
|
||||
(__ctype_lookup(__x)&_B) || (int) (__x) == '\t';})
|
||||
#endif
|
||||
/* Non-gcc versions will get the library versions, and will be
|
||||
slightly slower. These macros are not NLS-aware so they are
|
||||
disabled if the system supports the extended character sets. */
|
||||
# if defined(__GNUC__)
|
||||
# if !defined (_MB_EXTENDED_CHARSETS_ISO) && !defined (_MB_EXTENDED_CHARSETS_WINDOWS)
|
||||
# define toupper(__c) \
|
||||
__extension__ ({ __typeof__ (__c) __x = (__c); \
|
||||
islower (__x) ? (int) __x - 'a' + 'A' : (int) __x;})
|
||||
# define tolower(__c) \
|
||||
__extension__ ({ __typeof__ (__c) __x = (__c); \
|
||||
isupper (__x) ? (int) __x - 'A' + 'a' : (int) __x;})
|
||||
# else /* _MB_EXTENDED_CHARSETS* */
|
||||
/* Allow a gcc warning if the user passed 'char', but defer to the
|
||||
function. */
|
||||
# define toupper(__c) \
|
||||
__extension__ ({ __typeof__ (__c) __x = (__c); \
|
||||
(void) __ctype_ptr__[__x]; (toupper) (__x);})
|
||||
# define tolower(__c) \
|
||||
__extension__ ({ __typeof__ (__c) __x = (__c); \
|
||||
(void) __ctype_ptr__[__x]; (tolower) (__x);})
|
||||
# endif /* _MB_EXTENDED_CHARSETS* */
|
||||
# endif /* __GNUC__ */
|
||||
#endif /* !__cplusplus */
|
||||
#ifndef __STRICT_ANSI__
|
||||
#define isascii(__c) ((unsigned)(__c)<=0177)
|
||||
#define toascii(__c) ((__c)&0177)
|
||||
#endif
|
||||
/* For C++ backward-compatibility only. */
|
||||
extern __IMPORT _CONST char _ctype_[];
|
||||
_END_STD_C
|
||||
#endif /* _CTYPE_H_ */
|
||||
219
apps/tbasic/main_nix.c
Normal file
219
apps/tbasic/main_nix.c
Normal file
@@ -0,0 +1,219 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "../core/main.h"
|
||||
#include "../core/utils.h"
|
||||
#include "../core/textual.h"
|
||||
#include "../core/tokens.h"
|
||||
#include "../core/extern.h"
|
||||
|
||||
#define VARS_SPACE_SIZE 512
|
||||
#define DATA_SPACE_SIZE 4096
|
||||
#define LINE_SIZE 80
|
||||
|
||||
char extraCmdArgCnt[] = {2, 2, 0};
|
||||
|
||||
char extraFuncArgCnt[] = {1, 2};
|
||||
|
||||
static char* commonStrings = CONST_COMMON_STRINGS;
|
||||
static char * parsingErrors = CONST_PARSING_ERRORS;
|
||||
|
||||
char dataSpace[DATA_SPACE_SIZE];
|
||||
char lineSpace[LINE_SIZE * 3];
|
||||
|
||||
static FILE* fCurrent;
|
||||
static short idCurrent = 0;
|
||||
|
||||
static struct termios oldTermSettings;
|
||||
|
||||
static void initSystem(void) {
|
||||
struct termios termSettings;
|
||||
tcgetattr(STDIN_FILENO, &oldTermSettings);
|
||||
termSettings = oldTermSettings;
|
||||
termSettings.c_lflag &= ~(ICANON | ECHO | ISIG);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &termSettings);
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
}
|
||||
|
||||
static void cleanup(void) {
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldTermSettings);
|
||||
}
|
||||
|
||||
short sysGetc(void) {
|
||||
struct pollfd fd;
|
||||
fd.fd = STDIN_FILENO;
|
||||
fd.events = POLLIN;
|
||||
if (!poll(&fd, 1, 0)) {
|
||||
return -1;
|
||||
}
|
||||
return getchar();
|
||||
}
|
||||
|
||||
void sysPutc(char c) {
|
||||
putchar(c);
|
||||
}
|
||||
|
||||
void sysEcho(char c) {
|
||||
if (c == '\b') {
|
||||
sysPutc(c);
|
||||
sysPutc(' ');
|
||||
}
|
||||
sysPutc(c);
|
||||
}
|
||||
|
||||
void sysQuit(void) {
|
||||
cleanup();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void sysPoke(unsigned long addr, uchar value) {
|
||||
dataSpace[addr] = value;
|
||||
}
|
||||
|
||||
uchar sysPeek(unsigned long addr) {
|
||||
return dataSpace[addr];
|
||||
}
|
||||
|
||||
numeric sysMillis(numeric div) {
|
||||
struct timespec tp;
|
||||
long v;
|
||||
clock_gettime(CLOCK_REALTIME, &tp);
|
||||
v = (((numeric) tp.tv_sec) * 1000 + tp.tv_nsec / 1000000) & 0x7FFFFFFF;
|
||||
return div <= 1 ? v : v / div;
|
||||
}
|
||||
|
||||
char translateInput(short c) {
|
||||
if (c == -1) {
|
||||
c = 0;
|
||||
}
|
||||
return (char) (c & 0xFF);
|
||||
}
|
||||
|
||||
void outputConstStr(char strId, char index, char* w) {
|
||||
char* s;
|
||||
switch (strId) {
|
||||
case ID_COMMON_STRINGS:
|
||||
s = commonStrings;
|
||||
break;
|
||||
case ID_PARSING_ERRORS:
|
||||
s = parsingErrors;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
while (index > 0) {
|
||||
while (*s++ != '\n') {
|
||||
}
|
||||
index -= 1;
|
||||
}
|
||||
while (*s != '\n') {
|
||||
if (w) {
|
||||
*(w++) = (*s++);
|
||||
} else {
|
||||
sysPutc(*s++);
|
||||
}
|
||||
}
|
||||
if (w) {
|
||||
*w = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static numeric power(numeric base, numeric exp) {
|
||||
return exp < 1 ? 1 : base * power(base, exp - 1);
|
||||
}
|
||||
|
||||
short extraCommandByHash(numeric h) {
|
||||
switch (h) {
|
||||
case 0x036F: // POKE
|
||||
return CMD_EXTRA + 0;
|
||||
case 0x019C: // PIN - just prints argument values for test
|
||||
return CMD_EXTRA + 1;
|
||||
case 0x031A: // QUIT
|
||||
return CMD_EXTRA + 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
short extraFunctionByHash(numeric h) {
|
||||
switch (h) {
|
||||
case 0x0355: // PEEK
|
||||
return 0;
|
||||
case 0x06FC: // POWER - for test purpose
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void extraCommand(char cmd, numeric args[]) {
|
||||
switch (cmd) {
|
||||
case 0:
|
||||
sysPoke(args[0], args[1]);
|
||||
break;
|
||||
case 1:
|
||||
printf("PIN: %d,%d\n", args[0], args[1]);
|
||||
break;
|
||||
case 2:
|
||||
sysQuit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
numeric extraFunction(char cmd, numeric args[]) {
|
||||
switch (cmd) {
|
||||
case 0:
|
||||
return sysPeek(args[0]);
|
||||
case 1:
|
||||
return power(args[1], args[0]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char openStorage(char id, char op) {
|
||||
char fname[] = "store0.dat";
|
||||
char ops[] = "xb";
|
||||
fname[5] += id;
|
||||
ops[0] = op;
|
||||
fCurrent = fopen(fname, ops);
|
||||
return fCurrent != NULL ? 1 : 0;
|
||||
}
|
||||
|
||||
char storageOperation(void* data, short size) {
|
||||
if (data == NULL) {
|
||||
if (idCurrent != 0) {
|
||||
fclose(fCurrent);
|
||||
}
|
||||
idCurrent = 0;
|
||||
if (size != 0) {
|
||||
idCurrent = abs(size);
|
||||
if (!openStorage(idCurrent, size > 0 ? 'w' : 'r')) {
|
||||
idCurrent = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (size > 0) {
|
||||
fwrite(data, size, 1, fCurrent);
|
||||
} else {
|
||||
fread(data, -size, 1, fCurrent);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
initSystem();
|
||||
init(VARS_SPACE_SIZE, 80, sizeof(dataSpace) - VARS_SPACE_SIZE);
|
||||
while(1) {
|
||||
lastInput = translateInput(sysGetc());
|
||||
dispatch();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
21
apps/tbasic/mystdlib.h
Normal file
21
apps/tbasic/mystdlib.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef __MY_STDLIB_H_
|
||||
#define __MY_STDLIB_H_
|
||||
|
||||
#ifndef NO_STDLIB
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#else
|
||||
|
||||
#define NULL ( (void *) 0)
|
||||
|
||||
int strlen(const char* s);
|
||||
void* memcpy(void* dst, const void* src, int sz);
|
||||
int memcmp(const void* dst, const void* src, int sz);
|
||||
void* memmove(void* dst, const void* src, int sz);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
55
apps/tbasic/mytypes.h
Normal file
55
apps/tbasic/mytypes.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef __MYTYPES_H_
|
||||
#define __MYTYPES_H_
|
||||
|
||||
#define MAX_LINE_LEN 80
|
||||
|
||||
#define STATE_INTERACTIVE 0x00
|
||||
#define STATE_DELAY 0x01
|
||||
#define STATE_INPUT 0x02
|
||||
#define STATE_BREAK 0x03
|
||||
#define STATE_SLOWED (STATE_DELAY | STATE_INPUT | STATE_BREAK)
|
||||
#define STATE_PRELOAD 0x04
|
||||
#define STATE_RUN 0x10
|
||||
#define STATE_STEPS 0x20
|
||||
|
||||
//16 bit for arduino, 32 on stm32 and linux
|
||||
typedef signed int numeric;
|
||||
|
||||
typedef signed char schar;
|
||||
|
||||
typedef unsigned char uchar;
|
||||
|
||||
typedef struct nstring {
|
||||
unsigned char len;
|
||||
char text[1];
|
||||
} __attribute__((packed)) nstring;
|
||||
|
||||
typedef struct prgline {
|
||||
short num;
|
||||
nstring str;
|
||||
} __attribute__((packed)) prgline;
|
||||
|
||||
typedef union tbody {
|
||||
numeric integer;
|
||||
char symbol;
|
||||
unsigned char command;
|
||||
nstring str;
|
||||
} tbody;
|
||||
|
||||
typedef struct token {
|
||||
char type;
|
||||
tbody body;
|
||||
} __attribute__((packed)) token;
|
||||
|
||||
typedef struct varHolder {
|
||||
short name;
|
||||
numeric value;
|
||||
} __attribute__((packed)) varHolder;
|
||||
|
||||
typedef struct labelCacheElem {
|
||||
short num;
|
||||
short offset;
|
||||
} __attribute__((packed)) labelCacheElem;
|
||||
|
||||
#endif
|
||||
|
||||
BIN
apps/tbasic/store1.dat
Normal file
BIN
apps/tbasic/store1.dat
Normal file
Binary file not shown.
143
apps/tbasic/sysutils.c
Normal file
143
apps/tbasic/sysutils.c
Normal file
@@ -0,0 +1,143 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Name: sysutils.c
|
||||
// Created: January 2019
|
||||
// Author(s): Philip Smart
|
||||
// Description: Utilities for C compilation when stdlib is not included in the linker list.
|
||||
//
|
||||
// Credits:
|
||||
// Copyright: (c) 2019 Philip Smart <philip.smart@net2net.org>
|
||||
//
|
||||
// History: January 2019 - Initial script written.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// This source file is free software: you can redistribute it and#or modify
|
||||
// it under the terms of the GNU General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This source file is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__K64F__)
|
||||
// #include <stdio.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <string.h>
|
||||
#define uint32_t __uint32_t
|
||||
#define uint16_t __uint16_t
|
||||
#define uint8_t __uint8_t
|
||||
#define int32_t __int32_t
|
||||
#define int16_t __int16_t
|
||||
#define int8_t __int8_t
|
||||
#elif defined(__ZPU__)
|
||||
#include <zstdio.h>
|
||||
#include <zpu-types.h>
|
||||
#include <stdlib.h>
|
||||
#else
|
||||
#error "Target CPU not defined, use __ZPU__ or __K64F__"
|
||||
#endif
|
||||
//#include "interrupts.h"
|
||||
//#include "ff.h" /* Declarations of FatFs API */
|
||||
//#include "diskio.h"
|
||||
//#include <string.h>
|
||||
//#include <fcntl.h>
|
||||
//#include <sys/stat.h>
|
||||
//#include "xprintf.h"
|
||||
//#include "utils.h"
|
||||
//#include "basic_main.h"
|
||||
//#include "basic_utils.h"
|
||||
//#include "basic_textual.h"
|
||||
//#include "basic_tokens.h"
|
||||
//#include "basic_extern.h"
|
||||
////
|
||||
//#if defined __ZPUTA__
|
||||
// #include "zputa_app.h"
|
||||
//#elif defined __ZOS__
|
||||
// #include "zOS_app.h"
|
||||
//#else
|
||||
// #error OS not defined, use __ZPUTA__ or __ZOS__
|
||||
//#endif
|
||||
|
||||
int strlen(const char* s) {
|
||||
int i;
|
||||
for (i = 0; s[i]; i++) {
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void* memcpy(void* dst, const void* src, int sz) {
|
||||
char* d = (char*) dst;
|
||||
char* s = (char*) src;
|
||||
while (sz-- > 0) {
|
||||
*(d++) = *(s++);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
int memcmp(const void* dst, const void* src, int sz) {
|
||||
unsigned char* d = (unsigned char*) dst;
|
||||
unsigned char* s = (unsigned char*) src;
|
||||
int i, v;
|
||||
for (i = 0; i < sz; i++) {
|
||||
v = *(d++) - *(s++);
|
||||
if (v != 0) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* memmove(void* dst, const void* src, int sz) {
|
||||
unsigned char* d = (unsigned char*) dst;
|
||||
unsigned char* s = (unsigned char*) src;
|
||||
int i;
|
||||
if (d < s) {
|
||||
for (i = 0; i < sz; i++) {
|
||||
*(d++) = *(s++);
|
||||
}
|
||||
} else {
|
||||
d += sz;
|
||||
s += sz;
|
||||
for (i = 0; i < sz; i++) {
|
||||
*(--d) = *(--s);
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
int strcmp (const char *p1, const char *p2)
|
||||
{
|
||||
const unsigned char *s1 = (const unsigned char *) p1;
|
||||
const unsigned char *s2 = (const unsigned char *) p2;
|
||||
unsigned char c1, c2;
|
||||
do
|
||||
{
|
||||
c1 = (unsigned char) *s1++;
|
||||
c2 = (unsigned char) *s2++;
|
||||
if (c1 == '\0')
|
||||
return c1 - c2;
|
||||
}
|
||||
while (c1 == c2);
|
||||
return c1 - c2;
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
void _exit(int status)
|
||||
{
|
||||
while (1);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
349
apps/tbasic/tbasic.c
Executable file
349
apps/tbasic/tbasic.c
Executable file
@@ -0,0 +1,349 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Name: tbasic.c
|
||||
// Created: April 2029
|
||||
// Author(s): Miskatino (Miskatino Basic), RodionGork (TinyBasic), Philip Smart (zOS)
|
||||
// Description: Standalone App for the zOS/ZPU test application to provide a basic interpreter.
|
||||
// This program implements a loadable appliation which can be loaded from SD card by
|
||||
// the zOS/ZPUTA application. The idea is that commands or programs can be stored on the
|
||||
// SD card and executed by zOS/ZPUTA just like an OS such as Linux. The primary purpose
|
||||
// is to be able to minimise the size of zOS/ZPUTA for applications where minimal ram is
|
||||
// available.
|
||||
// Basic is a very useable language to make quick tests and thus I have ported the
|
||||
// TinyBasic/Miskatino fork to the ZPU/K64F and enhanced accordingly.
|
||||
//
|
||||
// Credits:
|
||||
// Copyright: (C) Miskatino MiskatinoBasic -> 2018, (C) RodionGork TinyBasic-> 2017
|
||||
// (c) 2019-2020 Philip Smart <philip.smart@net2net.org> zOS/ZPUTA Basic
|
||||
//
|
||||
// History: April 2020 - Ported the Miskatino version to work on the ZPU and K64F, updatesdto
|
||||
// function with the K64F processor and zOS.
|
||||
//
|
||||
// Notes: See Makefile to enable/disable conditional components
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// This source file is free software: you can redistribute it and#or modify
|
||||
// it under the terms of the GNU General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This source file is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__K64F__)
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "k64f_soc.h"
|
||||
#define uint32_t __uint32_t
|
||||
#define uint16_t __uint16_t
|
||||
#define uint8_t __uint8_t
|
||||
#define int32_t __int32_t
|
||||
#define int16_t __int16_t
|
||||
#define int8_t __int8_t
|
||||
#elif defined(__ZPU__)
|
||||
#include <zstdio.h>
|
||||
#include <zpu-types.h>
|
||||
#include "zpu_soc.h"
|
||||
#include <stdlib.h>
|
||||
#else
|
||||
#error "Target CPU not defined, use __ZPU__ or __K64F__"
|
||||
#endif
|
||||
#include "interrupts.h"
|
||||
#include "ff.h" /* Declarations of FatFs API */
|
||||
#include "diskio.h"
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include "xprintf.h"
|
||||
#include "utils.h"
|
||||
#include "basic_main.h"
|
||||
#include "basic_utils.h"
|
||||
#include "basic_textual.h"
|
||||
#include "basic_tokens.h"
|
||||
#include "basic_extern.h"
|
||||
//
|
||||
#if defined __ZPUTA__
|
||||
#include "zputa_app.h"
|
||||
#elif defined __ZOS__
|
||||
#include "zOS_app.h"
|
||||
#else
|
||||
#error OS not defined, use __ZPUTA__ or __ZOS__
|
||||
#endif
|
||||
//
|
||||
#include "tbasic.h"
|
||||
|
||||
|
||||
//#include <stdio.h>
|
||||
//#include <stdlib.h>
|
||||
//#include <signal.h>
|
||||
//#include <termios.h>
|
||||
//#include <unistd.h>
|
||||
//#include <poll.h>
|
||||
//#include <time.h>
|
||||
|
||||
// Utility functions.
|
||||
#include "tools.c"
|
||||
|
||||
// Version info.
|
||||
#define VERSION "v1.0"
|
||||
#define VERSION_DATE "10/04/2020"
|
||||
#define APP_NAME "TBASIC"
|
||||
|
||||
#define VARS_SPACE_SIZE 512
|
||||
#define DATA_SPACE_SIZE 4096
|
||||
#define LINE_SIZE 80
|
||||
|
||||
char extraCmdArgCnt[] = {2, 2, 0};
|
||||
char extraFuncArgCnt[] = {1, 2};
|
||||
|
||||
static char* commonStrings = CONST_COMMON_STRINGS;
|
||||
static char * parsingErrors = CONST_PARSING_ERRORS;
|
||||
static short doExit = 0;
|
||||
|
||||
char dataSpace[DATA_SPACE_SIZE];
|
||||
char lineSpace[LINE_SIZE * 3];
|
||||
|
||||
static FIL fCurrent;
|
||||
static short idCurrent = 0;
|
||||
|
||||
short sysGetc(void)
|
||||
{
|
||||
short keyIn;
|
||||
|
||||
#if defined __K64F__
|
||||
keyIn = (short)usb_serial_getchar();
|
||||
#elif defined __ZPU__
|
||||
keyIn = (short)getserial_nonblocking();
|
||||
#else
|
||||
#error "Target CPU not defined, use __ZPU__ or __K64F__"
|
||||
#endif
|
||||
|
||||
return keyIn;
|
||||
}
|
||||
|
||||
void sysPutc(char c)
|
||||
{
|
||||
xputc(c);
|
||||
}
|
||||
|
||||
void sysEcho(char c)
|
||||
{
|
||||
if (c == '\b') {
|
||||
sysPutc(c);
|
||||
sysPutc(' ');
|
||||
}
|
||||
sysPutc(c);
|
||||
}
|
||||
|
||||
void sysPoke(unsigned long addr, uchar value)
|
||||
{
|
||||
dataSpace[addr] = value;
|
||||
}
|
||||
|
||||
uchar sysPeek(unsigned long addr)
|
||||
{
|
||||
return dataSpace[addr];
|
||||
}
|
||||
|
||||
numeric sysMillis(numeric div)
|
||||
{
|
||||
numeric milliSec;
|
||||
|
||||
#if defined __ZPU__
|
||||
milliSec = (numeric)RTC_MILLISECONDS;
|
||||
#elif defined __K64F__
|
||||
milliSec = (numeric)*G->millis;
|
||||
#else
|
||||
#error "Target CPU not defined, use __ZPU__ or __K64F__"
|
||||
#endif
|
||||
return milliSec;
|
||||
}
|
||||
|
||||
char translateInput(short c)
|
||||
{
|
||||
if (c == -1) {
|
||||
c = 0;
|
||||
}
|
||||
return (char) (c & 0xFF);
|
||||
}
|
||||
|
||||
void outputConstStr(char strId, char index, char* w)
|
||||
{
|
||||
char* s;
|
||||
switch (strId)
|
||||
{
|
||||
case ID_COMMON_STRINGS:
|
||||
s = commonStrings;
|
||||
break;
|
||||
case ID_PARSING_ERRORS:
|
||||
s = parsingErrors;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
while (index > 0)
|
||||
{
|
||||
while (*s++ != '\n')
|
||||
{
|
||||
}
|
||||
index -= 1;
|
||||
}
|
||||
while (*s != '\n')
|
||||
{
|
||||
if (w)
|
||||
{
|
||||
*(w++) = (*s++);
|
||||
} else
|
||||
{
|
||||
sysPutc(*s++);
|
||||
}
|
||||
}
|
||||
if (w)
|
||||
{
|
||||
*w = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static numeric power(numeric base, numeric exp)
|
||||
{
|
||||
return exp < 1 ? 1 : base * power(base, exp - 1);
|
||||
}
|
||||
|
||||
short extraCommandByHash(numeric h)
|
||||
{
|
||||
switch (h)
|
||||
{
|
||||
case 0x036F: // POKE
|
||||
return CMD_EXTRA + 0;
|
||||
case 0x019C: // PIN - just prints argument values for test
|
||||
return CMD_EXTRA + 1;
|
||||
case 0x031A: // QUIT
|
||||
return CMD_EXTRA + 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
short extraFunctionByHash(numeric h)
|
||||
{
|
||||
switch (h)
|
||||
{
|
||||
case 0x0355: // PEEK
|
||||
return 0;
|
||||
case 0x06FC: // POWER - for test purpose
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void extraCommand(char cmd, numeric args[])
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case 0:
|
||||
sysPoke(args[0], args[1]);
|
||||
break;
|
||||
case 1:
|
||||
xprintf("PIN: %d,%d\n", args[0], args[1]);
|
||||
break;
|
||||
case 2:
|
||||
doExit = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
numeric extraFunction(char cmd, numeric args[])
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case 0:
|
||||
return sysPeek(args[0]);
|
||||
case 1:
|
||||
return power(args[1], args[0]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char storageOperation(void* data, short size)
|
||||
{
|
||||
UINT bytes;
|
||||
FRESULT fr;
|
||||
char fname[] = "TBASIC_0.dat";
|
||||
fname[7] += idCurrent;
|
||||
|
||||
if (data == NULL)
|
||||
{
|
||||
if (idCurrent != 0)
|
||||
{
|
||||
f_close(&fCurrent);
|
||||
}
|
||||
idCurrent = 0;
|
||||
if (size != 0)
|
||||
{
|
||||
idCurrent = abs(size);
|
||||
if(size > 0)
|
||||
{
|
||||
fr = f_open(&fCurrent, fname, FA_CREATE_ALWAYS | FA_WRITE);
|
||||
xprintf("Writing \"%s\"\n", fname);
|
||||
} else
|
||||
{
|
||||
fr = f_open(&fCurrent, fname, FA_OPEN_EXISTING | FA_READ);
|
||||
xprintf("Reading \"%s\"\n", fname);
|
||||
}
|
||||
|
||||
if (fr != FR_OK)
|
||||
{
|
||||
printFSCode(fr);
|
||||
idCurrent = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (size > 0)
|
||||
{
|
||||
fr = f_write(&fCurrent, data, size, &bytes);
|
||||
} else
|
||||
{
|
||||
fr = f_read(&fCurrent, data, -size, &bytes);
|
||||
}
|
||||
if(fr) { printFSCode(fr); return 0; } else { return 1; }
|
||||
}
|
||||
|
||||
// Main entry and start point of a zOS/ZPUTA Application. Only 2 parameters are catered for and a 32bit return code, additional parameters can be added by changing the appcrt0.s
|
||||
// startup code to add them to the stack prior to app() call.
|
||||
//
|
||||
// Return code for the ZPU is saved in _memreg by the C compiler, this is transferred to _memreg in zOS/ZPUTA in appcrt0.s prior to return.
|
||||
// The K64F ARM processor uses the standard register passing conventions, return code is stored in R0.
|
||||
//
|
||||
uint32_t app(uint32_t param1, uint32_t param2)
|
||||
{
|
||||
// Initialisation.
|
||||
//
|
||||
//char *ptr = (char *)param1;
|
||||
|
||||
init(VARS_SPACE_SIZE, 80, sizeof(dataSpace) - VARS_SPACE_SIZE);
|
||||
while(doExit == 0)
|
||||
{
|
||||
lastInput = translateInput(sysGetc());
|
||||
dispatch();
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
126
apps/tbasic/tbasic.h
Executable file
126
apps/tbasic/tbasic.h
Executable file
@@ -0,0 +1,126 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Name: tbasic.h
|
||||
// Created: April 2029
|
||||
// Author(s): Miskatino (Miskatino Basic), RodionGork (TinyBasic), Philip Smart (zOS)
|
||||
// Description: Standalone App for the zOS/ZPU test application to provide a basic interpreter.
|
||||
// This program implements a loadable appliation which can be loaded from SD card by
|
||||
// the zOS/ZPUTA application. The idea is that commands or programs can be stored on the
|
||||
// SD card and executed by zOS/ZPUTA just like an OS such as Linux. The primary purpose
|
||||
// is to be able to minimise the size of zOS/ZPUTA for applications where minimal ram is
|
||||
// available.
|
||||
// Basic is a very useable language to make quick tests and thus I have ported the
|
||||
// TinyBasic/Miskatino fork to the ZPU/K64F and enhanced accordingly.
|
||||
//
|
||||
// Credits:
|
||||
// Copyright: (C) Miskatino MiskatinoBasic -> 2018, (C) RodionGork TinyBasic-> 2017
|
||||
// (c) 2019-2020 Philip Smart <philip.smart@net2net.org> zOS/ZPUTA Basic
|
||||
//
|
||||
// History: April 2020 - Ported the Miskatino version to work on the ZPU and K64F, updatesdto
|
||||
// function with the K64F processor and zOS.
|
||||
//
|
||||
// Notes: See Makefile to enable/disable conditional components
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// This source file is free software: you can redistribute it and#or modify
|
||||
// it under the terms of the GNU General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This source file is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef TBASIC_H
|
||||
#define TBASIC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Constants.
|
||||
|
||||
// Application execution constants.
|
||||
//
|
||||
|
||||
// Components to be embedded in the program.
|
||||
//
|
||||
#define BUILTIN_DEFAULT 1
|
||||
// Disk low level components to be embedded in the program.
|
||||
#define BUILTIN_DISK_DUMP 0
|
||||
#define BUILTIN_DISK_STATUS 0
|
||||
// Disk buffer components to be embedded in the program.
|
||||
#define BUILTIN_BUFFER_DUMP 0
|
||||
#define BUILTIN_BUFFER_EDIT 0
|
||||
#define BUILTIN_BUFFER_READ 0
|
||||
#define BUILTIN_BUFFER_WRITE 0
|
||||
#define BUILTIN_BUFFER_FILL 0
|
||||
#define BUILTIN_BUFFER_LEN 0
|
||||
// Memory components to be embedded in the program.
|
||||
#define BUILTIN_MEM_CLEAR 0
|
||||
#define BUILTIN_MEM_COPY 0
|
||||
#define BUILTIN_MEM_DIFF 0
|
||||
#define BUILTIN_MEM_DUMP 0
|
||||
#define BUILTIN_MEM_PERF 0
|
||||
#define BUILTIN_MEM_SRCH 0
|
||||
#define BUILTIN_MEM_TEST 0
|
||||
#define BUILTIN_MEM_EDIT_BYTES 0
|
||||
#define BUILTIN_MEM_EDIT_HWORD 0
|
||||
#define BUILTIN_MEM_EDIT_WORD 0
|
||||
// Hardware components to be embedded in the program.
|
||||
#define BUILTIN_HW_SHOW_REGISTER 0
|
||||
#define BUILTIN_HW_TEST_TIMERS 0
|
||||
// Filesystem components to be embedded in the program.
|
||||
#define BUILTIN_FS_STATUS 0
|
||||
#define BUILTIN_FS_DIRLIST 1
|
||||
#define BUILTIN_FS_OPEN 0
|
||||
#define BUILTIN_FS_CLOSE 0
|
||||
#define BUILTIN_FS_SEEK 0
|
||||
#define BUILTIN_FS_READ 0
|
||||
#define BUILTIN_FS_CAT 0
|
||||
#define BUILTIN_FS_INSPECT 0
|
||||
#define BUILTIN_FS_WRITE 0
|
||||
#define BUILTIN_FS_TRUNC 0
|
||||
#define BUILTIN_FS_RENAME 0
|
||||
#define BUILTIN_FS_DELETE 0
|
||||
#define BUILTIN_FS_CREATEDIR 0
|
||||
#define BUILTIN_FS_ALLOCBLOCK 0
|
||||
#define BUILTIN_FS_CHANGEATTRIB 0
|
||||
#define BUILTIN_FS_CHANGETIME 0
|
||||
#define BUILTIN_FS_COPY 0
|
||||
#define BUILTIN_FS_CHANGEDIR 0
|
||||
#define BUILTIN_FS_CHANGEDRIVE 0
|
||||
#define BUILTIN_FS_SHOWDIR 0
|
||||
#define BUILTIN_FS_SETLABEL 0
|
||||
#define BUILTIN_FS_CREATEFS 0
|
||||
#define BUILTIN_FS_LOAD 0
|
||||
#define BUILTIN_FS_DUMP 0
|
||||
#define BUILTIN_FS_CONCAT 0
|
||||
#define BUILTIN_FS_XTRACT 0
|
||||
#define BUILTIN_FS_SAVE 0
|
||||
#define BUILTIN_FS_EXEC 0
|
||||
// Test components to be embedded in the program.
|
||||
#define BUILTIN_TST_DHRYSTONE 0
|
||||
#define BUILTIN_TST_COREMARK 0
|
||||
// Miscellaneous components to be embedded in this program.
|
||||
#define BUILTIN_MISC_HELP 0
|
||||
#define BUILTIN_MISC_SETTIME 0
|
||||
|
||||
// Prototypes.
|
||||
uint32_t app(uint32_t, uint32_t);
|
||||
|
||||
// Global scope variables within the ZPUTA memory space.
|
||||
GLOBALS *G;
|
||||
SOC_CONFIG *cfgSoC;
|
||||
|
||||
// Global scope variables in the app memory space.
|
||||
volatile UINT Timer; /* Performance timer (100Hz increment) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // TBASIC_H
|
||||
Reference in New Issue
Block a user