mirror of
https://github.com/clockworkpi/PicoCalc.git
synced 2025-12-12 10:18:54 +01:00
3680 lines
140 KiB
C
3680 lines
140 KiB
C
/***********************************************************************************************************************
|
|
PicoMite MMBasic
|
|
|
|
* @file commands.c
|
|
|
|
<COPYRIGHT HOLDERS> @author Geoff Graham, Peter Mather
|
|
Copyright (c) 2021, <COPYRIGHT HOLDERS> All rights reserved.
|
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
|
in the documentation and/or other materials provided with the distribution.
|
|
3. The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed
|
|
on the console at startup (additional copyright messages may be added).
|
|
4. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed
|
|
by the <copyright holder>.
|
|
5. Neither the name of the <copyright holder> nor the names of its contributors may be used to endorse or promote products derived from this software
|
|
without specific prior written permission.
|
|
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDERS> AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDERS> BE LIABLE FOR ANY DIRECT,
|
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
************************************************************************************************************************/
|
|
/**
|
|
* @file Commands.c
|
|
* @author Geoff Graham, Peter Mather
|
|
* @brief Source for standard MMBasic commands
|
|
*/
|
|
/**
|
|
* @cond
|
|
* The following section will be excluded from the documentation.
|
|
*/
|
|
|
|
|
|
#include "MMBasic_Includes.h"
|
|
#include "Hardware_Includes.h"
|
|
#include "hardware/flash.h"
|
|
#include "hardware/dma.h"
|
|
#include "hardware/structs/watchdog.h"
|
|
#ifdef PICOMITE
|
|
#include "pico/multicore.h"
|
|
#endif
|
|
#define overlap (VRes % (FontTable[gui_font >> 4][1] * (gui_font & 0b1111)) ? 0 : 1)
|
|
#include <math.h>
|
|
void flist(int, int, int);
|
|
//void clearprog(void);
|
|
char *KeyInterrupt=NULL;
|
|
unsigned char* SaveNextDataLine = NULL;
|
|
void execute_one_command(unsigned char *p);
|
|
void ListNewLine(int *ListCnt, int all);
|
|
int printWrappedText(const char *text, int screenWidth, int listcnt, int all) ;
|
|
char MMErrMsg[MAXERRMSG]; // the error message
|
|
volatile bool Keycomplete=false;
|
|
int keyselect=0;
|
|
extern volatile unsigned int ScrewUpTimer;
|
|
int SaveNextData = 0;
|
|
struct sa_data datastore[MAXRESTORE];
|
|
int restorepointer = 0;
|
|
uint64_t g_flag=0;
|
|
const uint8_t pinlist[]={ //this is a Basic program to print out the status of all the pins
|
|
1,132,128,95,113,37,0,
|
|
1,153,128,95,113,37,144,48,32,204,32,241,109,97,120,32,103,112,41,0,
|
|
1,168,128,34,71,80,34,130,186,95,113,37,41,44,32,241,112,105,110,110,111,32,34,71,80,34,130,186,
|
|
95,113,37,41,41,44,241,112,105,110,32,241,112,105,110,110,111,32,34,71,80,34,130,186,95,113,37,41,41,41,0,
|
|
1,166,128,0,
|
|
1,147,128,95,113,37,0,0
|
|
};
|
|
const uint8_t i2clist[]={ //this is a Basic program to print out the I2C devices connected to the SYSTEM I2C pins
|
|
1, 132, 128, 105, 110, 116, 101, 103, 101, 114, 32, 95, 97, 100, 0,
|
|
1, 132, 128, 105, 110, 116, 101, 103, 101, 114, 32, 120, 95, 44, 121, 95, 0,
|
|
1, 168, 128, 34, 32, 72, 69, 88, 32, 32, 48, 32, 32, 49, 32, 32, 50, 32, 32, 51, 32, 32, 52, 32, 32, 53, 32, 32, 54, 32, 32, 55, 32, 32,
|
|
56, 32, 32, 57, 32, 32, 65, 32, 32, 66, 32, 32, 67, 32, 32, 68, 32, 32, 69, 32, 32, 70, 34, 0,
|
|
1, 153, 128, 121, 95, 32, 144, 32, 48, 32, 204, 32, 55, 0,
|
|
1, 168, 128, 34, 32, 34, 59, 32, 164, 121, 95, 44, 32, 49, 41, 59, 32, 34, 48, 58, 32, 34, 59, 0,
|
|
1, 153, 128, 120, 95, 32, 144, 32, 48, 32, 204, 32, 49, 53, 0,
|
|
1, 161, 128, 95, 97, 100, 32, 144, 32, 121, 95, 32, 133, 32, 49, 54, 32, 130, 32, 120, 95, 0,
|
|
1, 158, 128, 241, 83, 89, 83, 84, 69, 77, 32, 73, 50, 67, 41, 144, 34, 73, 50, 67, 34, 32, 203, 32, 228, 128, 99, 104,
|
|
101, 99, 107, 32, 95, 97, 100, 32, 199, 32, 229, 128, 32, 99, 104, 101, 99, 107, 32, 95, 97, 100, 0,
|
|
1, 158, 128, 243, 68, 41, 32, 144, 32, 48, 32, 203, 0,
|
|
1, 158, 128, 95, 97, 100, 32, 144, 32, 48, 32, 203, 32, 168, 128, 34, 45, 45, 32, 34, 59, 0,
|
|
1, 158, 128, 95, 97, 100, 32, 143, 32, 48, 32, 203, 32, 168, 128, 164, 95, 97, 100, 44, 32, 50, 41, 59, 34, 32, 34, 59, 0,
|
|
1, 139, 128, 0, 1, 168, 128, 34, 45, 45, 32, 34, 59, 0, 1, 143, 128, 0, 1, 166, 128, 120, 95, 0,
|
|
1, 168, 128, 0, 1, 166, 128, 121, 95, 0, 1, 147, 128, 120, 95, 44, 121, 95, 0,
|
|
1, 147, 128, 95, 97, 100, 0,0
|
|
};
|
|
// stack to keep track of nested FOR/NEXT loops
|
|
struct s_forstack g_forstack[MAXFORLOOPS + 1];
|
|
int g_forindex;
|
|
|
|
|
|
|
|
// stack to keep track of nested DO/LOOP loops
|
|
struct s_dostack g_dostack[MAXDOLOOPS];
|
|
int g_doindex; // counts the number of nested DO/LOOP loops
|
|
|
|
|
|
// stack to keep track of GOSUBs, SUBs and FUNCTIONs
|
|
unsigned char *gosubstack[MAXGOSUB];
|
|
unsigned char *errorstack[MAXGOSUB];
|
|
int gosubindex;
|
|
|
|
unsigned char g_DimUsed = false; // used to catch OPTION BASE after DIM has been used
|
|
|
|
int TraceOn; // used to track the state of TRON/TROFF
|
|
unsigned char *TraceBuff[TRACE_BUFF_SIZE];
|
|
int TraceBuffIndex; // used for listing the contents of the trace buffer
|
|
int OptionErrorSkip; // how to handle an error
|
|
int MMerrno; // the error number
|
|
unsigned char cmdlinebuff[STRINGSIZE];
|
|
const unsigned int CaseOption = 0xffffffff; // used to store the case of the listed output
|
|
|
|
static inline CommandToken commandtbl_decode(const unsigned char *p){
|
|
return ((CommandToken)(p[0] & 0x7f)) | ((CommandToken)(p[1] & 0x7f)<<7);
|
|
}
|
|
|
|
void __not_in_flash_func(cmd_null)(void) {
|
|
// do nothing (this is just a placeholder for commands that have no action)
|
|
}
|
|
/** @endcond */
|
|
/**
|
|
* This command increments an integer or a float or concatenates two strings
|
|
* @param a the integer, float or string to be changed
|
|
* @param b OPTIONAL for integers and floats - defaults to 1. Otherwise the amount to increment the number or the string to concatenate
|
|
*/
|
|
#ifdef rp2350
|
|
void MIPS16 __not_in_flash_func(cmd_inc)(void){
|
|
#else
|
|
#ifdef PICOMITEVGA
|
|
void MIPS16 cmd_inc(void){
|
|
#else
|
|
void MIPS16 __not_in_flash_func(cmd_inc)(void){
|
|
#endif
|
|
#endif
|
|
unsigned char *p, *q;
|
|
int vtype;
|
|
getargs(&cmdline,3,(unsigned char *)",");
|
|
if(argc==1){
|
|
p = findvar(argv[0], V_FIND);
|
|
if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
|
|
vtype = TypeMask(g_vartbl[g_VarIndex].type);
|
|
if(vtype & T_STR) error("Invalid variable"); // sanity check
|
|
if(vtype & T_NBR)
|
|
(*(MMFLOAT *)p) = (*(MMFLOAT *)p) + 1.0;
|
|
else if(vtype & T_INT)*(long long int *)p = *(long long int *)p + 1;
|
|
else error("Syntax");
|
|
} else {
|
|
p = findvar(argv[0], V_FIND);
|
|
if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
|
|
vtype = TypeMask(g_vartbl[g_VarIndex].type);
|
|
if(vtype & T_STR){
|
|
int size=g_vartbl[g_VarIndex].size;
|
|
q=getstring(argv[2]);
|
|
if(*p + *q > size) error("String too long");
|
|
Mstrcat(p, q);
|
|
} else if(vtype & T_NBR){
|
|
(*(MMFLOAT *)p) = (*(MMFLOAT *)p)+getnumber(argv[2]);
|
|
} else if(vtype & T_INT){
|
|
*(long long int *)p = *(long long int *)p+getinteger(argv[2]);
|
|
} else error("syntax");
|
|
}
|
|
}
|
|
// the PRINT command
|
|
void cmd_print(void) {
|
|
unsigned char *s, *p;
|
|
unsigned char *ss;
|
|
MMFLOAT f;
|
|
long long int i64;
|
|
int i, t, fnbr;
|
|
int docrlf; // this is used to suppress the cr/lf if needed
|
|
|
|
getargs(&cmdline, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)";,"); // this is a macro and must be the first executable stmt
|
|
|
|
// s = 0; *s = 56; // for testing the exception handler
|
|
|
|
docrlf = true;
|
|
|
|
if(argc > 0 && *argv[0] == '#') { // check if the first arg is a file number
|
|
argv[0]++;
|
|
if((*argv[0] == 'G') || (*argv[0] == 'g')){
|
|
argv[0]++;
|
|
if(!((*argv[0] == 'P') || (*argv[0] == 'p')))error("Syntax");
|
|
argv[0]++;
|
|
if(!((*argv[0] == 'S') || (*argv[0] == 's')))error("Syntax");
|
|
if(!GPSchannel) error("GPS not activated");
|
|
if(argc!=3) error("Only a single string parameter allowed");
|
|
p = argv[2];
|
|
t = T_NOTYPE;
|
|
p = evaluate(p, &f, &i64, &s, &t, true); // get the value and type of the argument
|
|
ss=(unsigned char *)s;
|
|
if(!(t & T_STR)) error("Only a single string parameter allowed");
|
|
int i,xsum=0;
|
|
if(ss[1]!='$' || ss[ss[0]]!='*')error("GPS command must start with dollar and end with star");
|
|
for(i=1;i<=ss[0];i++){
|
|
SerialPutchar(GPSchannel, s[i]);
|
|
if(s[i]=='$')xsum=0;
|
|
if(s[i]!='*')xsum ^=s[i];
|
|
}
|
|
i=xsum/16;
|
|
i=i+'0';
|
|
if(i>'9')i=i-'0'+'A';
|
|
SerialPutchar(GPSchannel, i);
|
|
i=xsum % 16;
|
|
i=i+'0';
|
|
if(i>'9')i=i-'0'+'A';
|
|
SerialPutchar(GPSchannel, i);
|
|
SerialPutchar(GPSchannel, 13);
|
|
SerialPutchar(GPSchannel, 10);
|
|
return;
|
|
} else {
|
|
fnbr = getinteger(argv[0]); // get the number
|
|
i = 1;
|
|
if(argc >= 2 && *argv[1] == ',') i = 2; // and set the next argument to be looked at
|
|
}
|
|
} else {
|
|
fnbr = 0; // no file number so default to the standard output
|
|
i = 0;
|
|
}
|
|
|
|
for(; i < argc; i++) { // step through the arguments
|
|
if(*argv[i] == ',') {
|
|
MMfputc('\t', fnbr); // print a tab for a comma
|
|
docrlf = false; // a trailing comma should suppress CR/LF
|
|
}
|
|
else if(*argv[i] == ';') {
|
|
docrlf = false; // other than suppress cr/lf do nothing for a semicolon
|
|
}
|
|
else { // we have a normal expression
|
|
p = argv[i];
|
|
while(*p) {
|
|
t = T_NOTYPE;
|
|
p = evaluate(p, &f, &i64, &s, &t, true); // get the value and type of the argument
|
|
if(t & T_NBR) {
|
|
*inpbuf = ' '; // preload a space
|
|
FloatToStr((char *)inpbuf + ((f >= 0) ? 1:0), f, 0, STR_AUTO_PRECISION, (unsigned char)' ');// if positive output a space instead of the sign
|
|
MMfputs((unsigned char *)CtoM(inpbuf), fnbr); // convert to a MMBasic string and output
|
|
} else if(t & T_INT) {
|
|
*inpbuf = ' '; // preload a space
|
|
IntToStr((char *)inpbuf + ((i64 >= 0) ? 1:0), i64, 10); // if positive output a space instead of the sign
|
|
MMfputs((unsigned char *)CtoM(inpbuf), fnbr); // convert to a MMBasic string and output
|
|
} else if(t & T_STR) {
|
|
MMfputs((unsigned char *)s, fnbr); // print if a string (s is a MMBasic string)
|
|
} else error("Attempt to print reserved word");
|
|
}
|
|
docrlf = true;
|
|
}
|
|
}
|
|
if(docrlf) MMfputs((unsigned char *)"\2\r\n", fnbr); // print the terminating cr/lf unless it has been suppressed
|
|
if(PrintPixelMode!=0)SSPrintString("\033[m");
|
|
PrintPixelMode=0;
|
|
}
|
|
void cmd_arrayset(void){
|
|
array_set(cmdline);
|
|
}
|
|
void array_set(unsigned char *tp){
|
|
MMFLOAT f;
|
|
long long int i64;
|
|
unsigned char *s;
|
|
#ifdef rp2350
|
|
int dims[MAXDIM]={0};
|
|
#else
|
|
short dims[MAXDIM]={0};
|
|
#endif
|
|
int i,t,copy,card1=1;
|
|
unsigned char size=0;
|
|
MMFLOAT *a1float=NULL;
|
|
int64_t *a1int=NULL;
|
|
unsigned char *a1str=NULL;
|
|
getargs(&tp, 3,(unsigned char *)",");
|
|
if(!(argc == 3)) error("Argument count");
|
|
findvar(argv[2], V_FIND | V_EMPTY_OK | V_NOFIND_ERR);
|
|
t=g_vartbl[g_VarIndex].type;
|
|
evaluate(argv[0], &f, &i64, &s, &t, false);
|
|
if(t & T_STR){
|
|
card1=parsestringarray(argv[2],&a1str,2,0, dims, true, &size);
|
|
copy=(int)size+1;
|
|
memset(a1str,0,copy*card1);
|
|
if(*s){
|
|
for(i=0; i< card1;i++){
|
|
Mstrcpy(&a1str[i*copy],s);
|
|
}
|
|
}
|
|
} else {
|
|
card1=parsenumberarray(argv[2],&a1float,&a1int,2,0, dims, true);
|
|
if(t & T_STR) error("Syntax");
|
|
|
|
if(a1float!=NULL){
|
|
for(i=0; i< card1;i++)*a1float++ = ((t & T_INT) ? (MMFLOAT)i64 : f);
|
|
} else {
|
|
for(i=0; i< card1;i++)*a1int++ = ((t & T_INT) ? i64 : FloatToInt64(f));
|
|
}
|
|
}
|
|
}
|
|
void cmd_add(void){
|
|
array_add(cmdline);
|
|
}
|
|
|
|
void array_add(unsigned char *tp){
|
|
MMFLOAT f;
|
|
long long int i64;
|
|
unsigned char *s;
|
|
#ifdef rp2350
|
|
int dims[MAXDIM]={0};
|
|
#else
|
|
short dims[MAXDIM]={0};
|
|
#endif
|
|
int i,t,card1=1, card2=1;
|
|
MMFLOAT *a1float=NULL,*a2float=NULL, scale;
|
|
int64_t *a1int=NULL, *a2int=NULL;
|
|
unsigned char *a1str=NULL, *a2str=NULL;
|
|
getargs(&tp, 5,(unsigned char *)",");
|
|
if(!(argc == 5)) error("Argument count");
|
|
findvar(argv[0], V_FIND | V_EMPTY_OK | V_NOFIND_ERR);
|
|
t=g_vartbl[g_VarIndex].type;
|
|
if(t & T_STR){
|
|
unsigned char size=0,size2=0;
|
|
unsigned char *toadd;
|
|
card1=parsestringarray(argv[0], &a1str, 1, 0,dims, false, &size);
|
|
evaluate(argv[2], &f, &i64, &s, &t, false);
|
|
if(!(t & T_STR)) error("Syntax");
|
|
toadd=getstring(argv[2]);
|
|
card2=parsestringarray(argv[4], &a2str, 3, 0,dims, true, &size2);
|
|
if(card1 != card2)error("Array size mismatch");
|
|
unsigned char *buff = GetTempMemory(STRINGSIZE); // this will last for the life of the command
|
|
int copy=size+1;
|
|
int copy2=size2+1;
|
|
for(i=0; i< card1;i++){
|
|
unsigned char *sarg1=a1str+i*copy;
|
|
unsigned char *sarg2=a2str+i*copy2;
|
|
if(*sarg1 + *toadd > size2) error("String too long");
|
|
Mstrcpy(buff, sarg1);
|
|
Mstrcat(buff, toadd);
|
|
Mstrcpy(sarg2,buff);
|
|
}
|
|
} else {
|
|
card1=parsenumberarray(argv[0], &a1float, &a1int, 1, 0,dims, false);
|
|
evaluate(argv[2], &f, &i64, &s, &t, false);
|
|
if(t & T_STR) error("Syntax");
|
|
scale=getnumber(argv[2]);
|
|
card2=parsenumberarray(argv[4], &a2float, &a2int, 3, 0,dims, true);
|
|
if(card1 != card2)error("Array size mismatch");
|
|
if(scale!=0.0){
|
|
if(a2float!=NULL && a1float!=NULL){
|
|
for(i=0; i< card1;i++)*a2float++ = ((t & T_INT) ? (MMFLOAT)i64 : f) + (*a1float++);
|
|
} else if(a2float!=NULL && a1float==NULL){
|
|
for(i=0; i< card1;i++)(*a2float++) = ((t & T_INT) ? (MMFLOAT)i64 : f) + ((MMFLOAT)*a1int++);
|
|
} else if(a2float==NULL && a1float!=NULL){
|
|
for(i=0; i< card1;i++)(*a2int++) = FloatToInt64(((t & T_INT) ? i64 : FloatToInt64(f)) + (*a1float++));
|
|
} else {
|
|
for(i=0; i< card1;i++)(*a2int++) = ((t & T_INT) ? i64 : FloatToInt64(f)) + (*a1int++);
|
|
}
|
|
} else {
|
|
if(a2float!=NULL && a1float!=NULL){
|
|
for(i=0; i< card1;i++)*a2float++ = *a1float++;
|
|
} else if(a2float!=NULL && a1float==NULL){
|
|
for(i=0; i< card1;i++)(*a2float++) = ((MMFLOAT)*a1int++);
|
|
} else if(a2float==NULL && a1float!=NULL){
|
|
for(i=0; i< card1;i++)(*a2int++) = FloatToInt64(*a1float++);
|
|
} else {
|
|
for(i=0; i< card1;i++)*a2int++ = *a1int++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void cmd_insert(void){
|
|
array_insert(cmdline);
|
|
}
|
|
void array_insert(unsigned char *tp){
|
|
int i, j, t, start, increment, dim[MAXDIM], pos[MAXDIM],off[MAXDIM], dimcount=0, target=-1;
|
|
int64_t *a1int=NULL,*a2int=NULL;
|
|
MMFLOAT *afloat=NULL;
|
|
unsigned char *a1str=NULL, *a2str=NULL;
|
|
unsigned char size=0,size2=0;
|
|
#ifdef rp2350
|
|
int dims[MAXDIM]={0};
|
|
#else
|
|
short dims[MAXDIM]={0};
|
|
#endif
|
|
getargs(&tp, 15,(unsigned char *)",");
|
|
if(argc<7)error("Argument count");
|
|
findvar(argv[0], V_FIND | V_EMPTY_OK | V_NOFIND_ERR);
|
|
t=g_vartbl[g_VarIndex].type;
|
|
if(t & T_STR){
|
|
parsestringarray(argv[0],&a1str,1,0,dims, false,&size);
|
|
} else {
|
|
parsenumberarray(argv[0],&afloat,&a1int,1,0,dims, false);
|
|
if(!a1int)a1int=(int64_t *)afloat;
|
|
}
|
|
if(dims[1]<=0)error("Argument 1 must be a 2D or more array");
|
|
for(i=0;i<MAXDIM;i++){
|
|
if(dims[i]-g_OptionBase>0){
|
|
dimcount++;
|
|
dim[i]=dims[i]-g_OptionBase;
|
|
} else dim[i]=0;
|
|
}
|
|
if(((argc-1)/2-1)!=dimcount)error("Argument count");
|
|
for(i=0; i<dimcount;i++ ){
|
|
if(*argv[i*2 +2]) pos[i]=getint(argv[i*2 +2],g_OptionBase,dim[i]+g_OptionBase)-g_OptionBase;
|
|
else {
|
|
if(target!=-1)error("Only one index can be omitted");
|
|
target=i;
|
|
pos[i]=1;
|
|
}
|
|
}
|
|
if(t & T_STR){
|
|
parsestringarray(argv[i*2 +2],&a2str,i+1,1,dims, true,&size2);
|
|
} else {
|
|
parsenumberarray(argv[i*2 +2],&afloat,&a2int,i+1,1,dims,true);
|
|
if(!a2int)a2int=(int64_t *)afloat;
|
|
}
|
|
if(target==-1)return;
|
|
if(dim[target]+g_OptionBase!=dims[0])error("Size mismatch between insert and target array");
|
|
if(size!=size2)error("String arrays differ in string length");
|
|
i=dimcount-1;
|
|
while(i>=0){
|
|
off[i]=1;
|
|
for(j=0; j<i; j++)off[i]*=(dim[j]+1);
|
|
i--;
|
|
}
|
|
start=1;
|
|
for(i=0;i<dimcount;i++){
|
|
start+= (pos[i]*off[i]);
|
|
}
|
|
start--;
|
|
increment=off[target];
|
|
start-=increment;
|
|
if(t & T_STR){
|
|
int copy=(int)size+1;
|
|
for(i=0;i<=dim[target];i++) {
|
|
unsigned char *p=a2str+i*copy;
|
|
unsigned char *q=&a1str[(start+i*increment)*copy];
|
|
memcpy(q,p,copy);
|
|
}
|
|
} else {
|
|
for(i=0;i<=dim[target];i++) a1int[start+i*increment]=*a2int++;
|
|
}
|
|
return;
|
|
|
|
}
|
|
void cmd_slice(void){
|
|
array_slice(cmdline);
|
|
}
|
|
void array_slice(unsigned char *tp){
|
|
int i, j, t, start, increment, dim[MAXDIM], pos[MAXDIM],off[MAXDIM], dimcount=0, target=-1, toarray=0;
|
|
int64_t *a1int=NULL,*a2int=NULL;
|
|
MMFLOAT *afloat=NULL;
|
|
unsigned char *a1str=NULL,*a2str=NULL;
|
|
unsigned char size=0,size2=0;
|
|
#ifdef rp2350
|
|
int dims[MAXDIM]={0};
|
|
#else
|
|
short dims[MAXDIM]={0};
|
|
#endif
|
|
getargs(&tp, 15,(unsigned char *)",");
|
|
if(argc<7)error("Argument count");
|
|
findvar(argv[0], V_FIND | V_EMPTY_OK | V_NOFIND_ERR);
|
|
t=g_vartbl[g_VarIndex].type;
|
|
if(t & T_STR){
|
|
parsestringarray(argv[0],&a1str,1,0,dims, false, &size);
|
|
} else {
|
|
parsenumberarray(argv[0],&afloat,&a1int,1,0,dims, false);
|
|
if(!a1int)a1int=(int64_t *)afloat;
|
|
}
|
|
if(dims[1]<=0)error("Argument 1 must be a 2D or more array");
|
|
for(i=0;i<MAXDIM;i++){
|
|
if(dims[i]-g_OptionBase>0){
|
|
dimcount++;
|
|
dim[i]=dims[i]-g_OptionBase;
|
|
} else dim[i]=0;
|
|
}
|
|
if(((argc-1)/2-1)!=dimcount)error("Argument count");
|
|
for(i=0; i<dimcount;i++ ){
|
|
if(*argv[i*2 +2]) pos[i]=getint(argv[i*2 +2],g_OptionBase,dim[i]+g_OptionBase)-g_OptionBase;
|
|
else {
|
|
if(target!=-1)error("Only one index can be omitted");
|
|
target=i;
|
|
pos[i]=1;
|
|
}
|
|
}
|
|
if(t & T_STR){
|
|
toarray=parsestringarray(argv[i*2 +2],&a2str,i+1,1,dims, true, &size2)-1;
|
|
} else {
|
|
toarray=parsenumberarray(argv[i*2 +2],&afloat,&a2int,i+1,1,dims, true)-1;
|
|
if(!a2int)a2int=(int64_t *)afloat;
|
|
}
|
|
if(dim[target]!=toarray)error("Size mismatch between slice and target array");
|
|
if(size!=size2)error("String arrays differ in string length");
|
|
i=dimcount-1;
|
|
while(i>=0){
|
|
off[i]=1;
|
|
for(j=0; j<i; j++)off[i]*=(dim[j]+1);
|
|
i--;
|
|
}
|
|
start=1;
|
|
for(i=0;i<dimcount;i++){
|
|
start+= (pos[i]*off[i]);
|
|
}
|
|
start--;
|
|
increment=off[target];
|
|
start-=increment;
|
|
if(t & T_STR){
|
|
int copy=(int)size+1; //allow for the length character of the string
|
|
for(i=0;i<=dim[target];i++){
|
|
unsigned char *p=a2str+i*copy;
|
|
unsigned char *q=&a1str[(start+i*increment)*copy];
|
|
memcpy(p,q,copy);
|
|
}
|
|
} else {
|
|
for(i=0;i<=dim[target];i++)*a2int++ = a1int[start+i*increment];
|
|
}
|
|
return;
|
|
}
|
|
|
|
// the LET command
|
|
// because the LET is implied (ie, line does not have a recognisable command)
|
|
// it ends up as the place where mistyped commands are discovered. This is why
|
|
// the error message is "Unknown command"
|
|
void MIPS16 __not_in_flash_func(cmd_let)(void) {
|
|
int t, size;
|
|
MMFLOAT f;
|
|
long long int i64;
|
|
unsigned char *s;
|
|
unsigned char *p1, *p2;
|
|
|
|
p1 = cmdline;
|
|
// search through the line looking for the equals sign
|
|
while(*p1 && tokenfunction(*p1) != op_equal) p1++;
|
|
if(!*p1) error("Unknown command");
|
|
|
|
// check that we have a straight forward variable
|
|
p2 = skipvar(cmdline, false);
|
|
skipspace(p2);
|
|
if(p1 != p2) error("Syntax");
|
|
|
|
// create the variable and get the length if it is a string
|
|
p2 = findvar(cmdline, V_FIND);
|
|
size = g_vartbl[g_VarIndex].size;
|
|
if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
|
|
|
|
// step over the equals sign, evaluate the rest of the command and save in the variable
|
|
p1++;
|
|
if(g_vartbl[g_VarIndex].type & T_STR) {
|
|
t = T_STR;
|
|
p1 = evaluate(p1, &f, &i64, &s, &t, false);
|
|
if(*s > size) error("String too long");
|
|
Mstrcpy(p2, s);
|
|
}
|
|
else if(g_vartbl[g_VarIndex].type & T_NBR) {
|
|
t = T_NBR;
|
|
p1 = evaluate(p1, &f, &i64, &s, &t, false);
|
|
if(t & T_NBR)
|
|
(*(MMFLOAT *)p2) = f;
|
|
else
|
|
(*(MMFLOAT *)p2) = (MMFLOAT)i64;
|
|
} else {
|
|
t = T_INT;
|
|
p1 = evaluate(p1, &f, &i64, &s, &t, false);
|
|
if(t & T_INT)
|
|
(*(long long int *)p2) = i64;
|
|
else
|
|
(*(long long int *)p2) = FloatToInt64(f);
|
|
}
|
|
checkend(p1);
|
|
}
|
|
/**
|
|
* @cond
|
|
* The following section will be excluded from the documentation.
|
|
*/
|
|
int MIPS16 as_strcmpi (const char *s1, const char *s2)
|
|
{
|
|
const unsigned char *p1 = (const unsigned char *) s1;
|
|
const unsigned char *p2 = (const unsigned char *) s2;
|
|
unsigned char c1, c2;
|
|
|
|
if (p1 == p2)
|
|
return 0;
|
|
|
|
do
|
|
{
|
|
c1 = tolower (*p1++);
|
|
c2 = tolower (*p2++);
|
|
if (c1 == '\0')
|
|
break;
|
|
}
|
|
while (c1 == c2);
|
|
|
|
return c1 - c2;
|
|
}
|
|
void MIPS16 sortStrings(char **arr, int n)
|
|
{
|
|
char temp[STRINGSIZE];
|
|
int i,j;
|
|
// Sorting strings using bubble sort
|
|
for (j=0; j<n-1; j++)
|
|
{
|
|
for (i=j+1; i<n; i++)
|
|
{
|
|
if (as_strcmpi(arr[j], arr[i]) > 0)
|
|
{
|
|
strcpy(temp, arr[j]);
|
|
strcpy(arr[j], arr[i]);
|
|
strcpy(arr[i], temp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void MIPS16 ListFile(char *pp, int all) {
|
|
char buff[STRINGSIZE];
|
|
int fnbr;
|
|
int i,ListCnt = CurrentY/(FontTable[gui_font >> 4][1] * (gui_font & 0b1111)) + 2;
|
|
fnbr = FindFreeFileNbr();
|
|
if(!BasicFileOpen(pp, fnbr, FA_READ)) return;
|
|
while(!FileEOF(fnbr)) { // while waiting for the end of file
|
|
memset(buff,0,256);
|
|
MMgetline(fnbr, (char *)buff); // get the input line
|
|
for(i=0;i<strlen(buff);i++)if(buff[i] == TAB) buff[i] = ' ';
|
|
ListCnt=printWrappedText(buff,Option.Width,ListCnt,all);
|
|
}
|
|
FileClose(fnbr);
|
|
}
|
|
|
|
void MIPS16 ListNewLine(int *ListCnt, int all) {
|
|
unsigned char noscroll=Option.NoScroll;
|
|
if(!all && (void *)ReadBuffer!=(void *)DisplayNotSet)Option.NoScroll=0;
|
|
MMPrintString("\r\n");
|
|
(*ListCnt)++;
|
|
if(!all && *ListCnt >= Option.Height-overlap) {
|
|
#ifdef USBKEYBOARD
|
|
clearrepeat();
|
|
#endif
|
|
MMPrintString("PRESS ANY KEY ...");
|
|
MMgetchar();
|
|
MMPrintString("\r \r");
|
|
if(Option.DISPLAY_CONSOLE){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;}
|
|
*ListCnt = 2;
|
|
}
|
|
Option.NoScroll=noscroll;
|
|
}
|
|
|
|
|
|
void MIPS16 ListProgram(unsigned char *p, int all) {
|
|
char b[STRINGSIZE];
|
|
char *pp;
|
|
int ListCnt = CurrentY/(FontTable[gui_font >> 4][1] * (gui_font & 0b1111)) + 2;
|
|
while(!(*p == 0 || *p == 0xff)) { // normally a LIST ends at the break so this is a safety precaution
|
|
if(*p == T_NEWLINE) {
|
|
p = llist((unsigned char *)b, p); // otherwise expand the line
|
|
if(!(b[0]=='\'' && b[1]=='#')){
|
|
pp = b;
|
|
if(Option.continuation){
|
|
format_string(pp,Option.Width);
|
|
while(*pp) {
|
|
if(*pp=='\n'){
|
|
ListNewLine(&ListCnt, all);
|
|
pp++;
|
|
continue;
|
|
}
|
|
MMputchar(*pp++,0);
|
|
}
|
|
} else {
|
|
while(*pp) {
|
|
if(MMCharPos > Option.Width) ListNewLine(&ListCnt, all);
|
|
MMputchar(*pp++,0);
|
|
}
|
|
}
|
|
fflush(stdout);
|
|
ListNewLine(&ListCnt, all);
|
|
if(p[0] == 0 && p[1] == 0) break; // end of the listing ?
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void MIPS16 do_run(unsigned char *cmdline, bool CMM2mode) {
|
|
// RUN [ filename$ ] [, cmd_args$ ]
|
|
unsigned char *filename = (unsigned char *)"", *cmd_args = (unsigned char *)"";
|
|
unsigned char *cmdbuf=GetMemory(256);
|
|
memcpy(cmdbuf,cmdline,STRINGSIZE);
|
|
getargs(&cmdbuf, 3, (unsigned char *)",");
|
|
switch (argc) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
filename = getCstring(argv[0]);
|
|
break;
|
|
case 2:
|
|
cmd_args = getCstring(argv[1]);
|
|
break;
|
|
default:
|
|
filename = getCstring(argv[0]);
|
|
if(*argv[2])cmd_args = getCstring(argv[2]);
|
|
break;
|
|
}
|
|
|
|
// The memory allocated by getCstring() is not preserved across
|
|
// a call to FileLoadProgram() so we need to cache 'filename' and
|
|
// 'cmd_args' on the stack.
|
|
unsigned char buf[MAXSTRLEN + 1];
|
|
if (snprintf((char *)buf, MAXSTRLEN + 1, "\"%s\",%s", filename, cmd_args) > MAXSTRLEN) {
|
|
error("RUN command line too long");
|
|
}
|
|
unsigned char *pcmd_args = buf + strlen((char *)filename) + 3; // *** THW 16/4/23
|
|
|
|
#ifdef rp2350
|
|
if(CMM2mode){
|
|
if (*filename && !FileLoadCMM2Program((char *)buf,false)) return;
|
|
} else {
|
|
#endif
|
|
if (*filename && !FileLoadProgram(buf, false)) return;
|
|
#ifdef rp2350
|
|
}
|
|
#endif
|
|
ClearRuntime(true);
|
|
PrepareProgram(true);
|
|
if(Option.DISPLAY_CONSOLE && (SPIREAD || Option.NoScroll)){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;}
|
|
// Create a global constant MM.CMDLINE$ containing 'cmd_args'.
|
|
// void *ptr = findvar((unsigned char *)"MM.CMDLINE$", V_FIND | V_DIM_VAR | T_CONST);
|
|
CtoM(pcmd_args);
|
|
// memcpy(cmdlinebuff, pcmd_args, *pcmd_args + 1); // *** THW 16/4/23
|
|
Mstrcpy(cmdlinebuff, pcmd_args);
|
|
IgnorePIN = false;
|
|
if(Option.LIBRARY_FLASH_SIZE == MAX_PROG_SIZE) ExecuteProgram(LibMemory ); // run anything that might be in the library
|
|
if(*ProgMemory != T_NEWLINE) return; // no program to run
|
|
#ifdef PICOMITEWEB
|
|
cleanserver();
|
|
#endif
|
|
#ifndef USBKEYBOARD
|
|
if(mouse0==false && Option.MOUSE_CLOCK)initMouse0(0); //see if there is a mouse to initialise
|
|
#endif
|
|
nextstmt = ProgMemory;
|
|
}
|
|
/** @endcond */
|
|
void MIPS16 cmd_list(void) {
|
|
unsigned char *p;
|
|
int i,j,k,m,step;
|
|
if((p = checkstring(cmdline, (unsigned char *)"ALL"))) {
|
|
if(!(*p == 0 || *p == '\'')) {
|
|
if(Option.DISPLAY_CONSOLE && (SPIREAD || Option.NoScroll)){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;}
|
|
getargs(&p,1,(unsigned char *)",");
|
|
char *buff=GetTempMemory(STRINGSIZE);
|
|
strcpy(buff,(char *)getCstring(argv[0]));
|
|
if(strchr(buff, '.') == NULL) strcat(buff, ".bas");
|
|
ListFile(buff, true);
|
|
} else {
|
|
if(Option.DISPLAY_CONSOLE && (SPIREAD || Option.NoScroll)){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;}
|
|
ListProgram(ProgMemory, true);
|
|
checkend(p);
|
|
}
|
|
} else if((p = checkstring(cmdline, (unsigned char *)"VARIABLES"))) {
|
|
int count=0;
|
|
int64_t *dest=NULL;
|
|
char *buff=NULL;
|
|
int j=0;
|
|
getargs(&p,1,(unsigned char *)",");
|
|
if(argc){
|
|
j=(parseintegerarray(argv[0],&dest,1,1,NULL,true)-1)*8;
|
|
dest[0] = 0;
|
|
}
|
|
for(int i=0;i<MAXVARS;i++){
|
|
if(g_vartbl[i].type & (T_INT|T_STR|T_NBR)){
|
|
count++;
|
|
}
|
|
}
|
|
if(!count)return;
|
|
char** c=GetTempMemory(count*sizeof(*c)+count*(MAXVARLEN+30));
|
|
for(int i=0,j=0;i<MAXVARS;i++){
|
|
char out[MAXVARLEN+30];
|
|
if(g_vartbl[i].type & (T_INT|T_STR|T_NBR)){
|
|
if(g_vartbl[i].level==0)strcpy(out,"DIM ");
|
|
else strcpy(out,"LOCAL ");
|
|
if(!(g_vartbl[i].type & T_EXPLICIT)){
|
|
if(g_vartbl[i].type & T_INT){
|
|
if(!(g_vartbl[i].type & T_EXPLICIT))strcat(out,"INTEGER ");
|
|
}
|
|
if(g_vartbl[i].type & T_STR){
|
|
if(!(g_vartbl[i].type & T_EXPLICIT))strcat(out,"STRING ");
|
|
}
|
|
if(g_vartbl[i].type & T_NBR){
|
|
if(!(g_vartbl[i].type & T_EXPLICIT))strcat(out,"FLOAT ");
|
|
}
|
|
}
|
|
strcat(out,(char *)g_vartbl[i].name);
|
|
if(g_vartbl[i].type & T_INT){
|
|
if(g_vartbl[i].type & T_EXPLICIT)strcat(out,"%");
|
|
}
|
|
if(g_vartbl[i].type & T_STR){
|
|
if(g_vartbl[i].type & T_EXPLICIT)strcat(out,"$");
|
|
}
|
|
if(g_vartbl[i].type & T_NBR){
|
|
if(g_vartbl[i].type & T_EXPLICIT)strcat(out,"!");
|
|
}
|
|
if(g_vartbl[i].dims[0]>0){
|
|
strcat(out,"(");
|
|
for(int k=0;k<MAXDIM;k++){
|
|
if(g_vartbl[i].dims[k]>0){
|
|
char s[20];
|
|
IntToStr(s, (int64_t)g_vartbl[i].dims[k], 10);
|
|
strcat(out,s) ;
|
|
}
|
|
if(k<MAXDIM-1 && g_vartbl[i].dims[k+1]>0)strcat(out,",");
|
|
}
|
|
strcat(out,")");
|
|
}
|
|
c[j]= (char *)((int)c + sizeof(char *) * count + j*(MAXVARLEN+30));
|
|
strcpy(c[j],out);
|
|
j++;
|
|
}
|
|
}
|
|
sortStrings(c,count);
|
|
int ListCnt = 2;
|
|
if(dest==NULL){
|
|
for(int i=0;i<count;i++){
|
|
MMPrintString(c[i]);
|
|
if(Option.DISPLAY_CONSOLE)ListNewLine(&ListCnt, 0);
|
|
else MMPrintString("\r\n");
|
|
}
|
|
} else {
|
|
int ol=0;
|
|
buff=(char *)&dest[1];
|
|
for(int i=0;i<count;i++){
|
|
if(ol+strlen(c[i])+2 > j)error("Destination array too small");
|
|
else ol+=strlen(c[i])+2;
|
|
strcat(buff,c[i]);
|
|
strcat(buff,"\r\n");
|
|
}
|
|
dest[0]=ol;
|
|
}
|
|
|
|
} else if((p = checkstring(cmdline, (unsigned char *)"PINS"))) {
|
|
#if defined(PICOMITEWEB) && defined(rp2350)
|
|
if(!rp2350a)error("Incompatible board, RP2350A only" );
|
|
#endif
|
|
CallExecuteProgram((char *)pinlist);
|
|
return;
|
|
} else if((p = checkstring(cmdline, (unsigned char *)"SYSTEM I2C"))) {
|
|
if(I2C0locked || I2C1locked)CallExecuteProgram((char *)i2clist);
|
|
else error("System I2c not defined");
|
|
return;
|
|
} else if((p = checkstring(cmdline, (unsigned char *)"COMMANDS"))) {
|
|
int ListCnt = 2;
|
|
step=Option.DISPLAY_CONSOLE ? HRes/gui_font_width/20 : 5;
|
|
if(Option.DISPLAY_CONSOLE && (SPIREAD || Option.NoScroll)){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;}
|
|
m=0;
|
|
int x=0;
|
|
char** c=GetTempMemory((CommandTableSize+x)*sizeof(*c)+(CommandTableSize+x)*18);
|
|
for(i=0;i<CommandTableSize+x;i++){
|
|
c[m]= (char *)((int)c + sizeof(char *) * (CommandTableSize+x) + m*18);
|
|
if(m<CommandTableSize)strcpy(c[m],(char *)commandtbl[i].name);
|
|
if(*c[m]=='_' && c[m][1]!='(')*c[m]='.';
|
|
m++;
|
|
}
|
|
sortStrings(c,m);
|
|
for(i=1;i<m;i+=step){
|
|
for(k=0;k<step;k++){
|
|
if(i+k<m){
|
|
MMPrintString(c[i+k]);
|
|
if(k!=(step-1))for(j=strlen(c[i+k]);j<15;j++)MMputchar(' ',1);
|
|
}
|
|
}
|
|
if(Option.DISPLAY_CONSOLE)ListNewLine(&ListCnt, 0);
|
|
else MMPrintString("\r\n");
|
|
}
|
|
MMPrintString("Total of ");PInt(m-1);MMPrintString(" commands\r\n");
|
|
} else if((p = checkstring(cmdline, (unsigned char *)"FUNCTIONS"))) {
|
|
m=0;
|
|
int ListCnt = 2;
|
|
step=Option.DISPLAY_CONSOLE ? HRes/gui_font_width/20 : 5;
|
|
if(Option.DISPLAY_CONSOLE && (SPIREAD || Option.NoScroll)){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;}
|
|
int x=3+MMEND;
|
|
char** c=GetTempMemory((TokenTableSize+x)*sizeof(*c)+(TokenTableSize+x)*20);
|
|
for(i=0;i<TokenTableSize+x;i++){
|
|
c[m]= (char *)((int)c + sizeof(char *) * (TokenTableSize+x) + m*20);
|
|
if(m<TokenTableSize)strcpy(c[m],(char *)tokentbl[i].name);
|
|
else if(m<TokenTableSize+MMEND && m>=TokenTableSize)strcpy(c[m],overlaid_functions[i-TokenTableSize]);
|
|
else if(m==TokenTableSize+MMEND)strcpy(c[m],"=<");
|
|
else if(m==TokenTableSize+MMEND+1)strcpy(c[m],"=>");
|
|
else strcpy(c[m],"MM.Info$(");
|
|
m++;
|
|
}
|
|
sortStrings(c,m);
|
|
for(i=1;i<m-1;i+=step){
|
|
for(k=0;k<step;k++){
|
|
if(i+k<m-1){
|
|
MMPrintString(c[i+k]);
|
|
if(k!=(step-1))for(j=strlen(c[i+k]);j<15;j++)MMputchar(' ',1);
|
|
}
|
|
}
|
|
if(Option.DISPLAY_CONSOLE)ListNewLine(&ListCnt, 0);
|
|
else MMPrintString("\r\n");
|
|
}
|
|
MMPrintString("Total of ");PInt(m-1);MMPrintString(" functions and operators\r\n");
|
|
} else {
|
|
if(!(*cmdline == 0 || *cmdline == '\'')) {
|
|
getargs(&cmdline,1,(unsigned char *)",");
|
|
if(Option.DISPLAY_CONSOLE && (SPIREAD || Option.NoScroll)){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;}
|
|
char *buff=GetTempMemory(STRINGSIZE);
|
|
strcpy(buff,(char *)getCstring(argv[0]));
|
|
if(strchr(buff, '.') == NULL) {
|
|
if(!ExistsFile(buff))strcat(buff, ".bas");
|
|
}
|
|
ListFile(buff, false);
|
|
} else {
|
|
if(Option.DISPLAY_CONSOLE && (SPIREAD || Option.NoScroll)){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;}
|
|
ListProgram(ProgMemory, false);
|
|
checkend(cmdline);
|
|
}
|
|
}
|
|
}
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
int printWrappedText(const char *text, int screenWidth, int listcnt, int all) {
|
|
int length = strlen(text);
|
|
int start = 0; // Start index of the current line
|
|
char buff[STRINGSIZE];
|
|
while (start < length) {
|
|
int end = start + screenWidth; // Calculate the end index for the current line
|
|
if (end >= length) {
|
|
// If end is beyond the text length, just print the remaining text
|
|
memset(buff,0,STRINGSIZE);
|
|
sprintf(buff,"%s", text + start);
|
|
MMPrintString(buff);
|
|
ListNewLine(&listcnt, all);
|
|
break;
|
|
}
|
|
|
|
// Find the last space within the current screen width
|
|
int lastSpace = -1;
|
|
for (int i = start; i < end; i++) {
|
|
if (text[i] == ' ') {
|
|
lastSpace = i;
|
|
}
|
|
}
|
|
|
|
if (lastSpace != -1) {
|
|
// If a space is found, break at the space
|
|
memset(buff,0,STRINGSIZE);
|
|
sprintf(buff, "%.*s", lastSpace - start, text + start);
|
|
MMPrintString(buff);
|
|
ListNewLine(&listcnt, all);
|
|
start = lastSpace + 1; // Skip the space
|
|
} else {
|
|
// If no space is found, truncate at screen width
|
|
memset(buff,0,STRINGSIZE);
|
|
sprintf(buff,"%.*s", screenWidth, text + start);
|
|
MMPrintString(buff);
|
|
ListNewLine(&listcnt, all);
|
|
start += screenWidth;
|
|
}
|
|
}
|
|
return listcnt;
|
|
}
|
|
|
|
void cmd_help(void){
|
|
getargs(&cmdline,1,(unsigned char *)",");
|
|
if(!ExistsFile("B:/help.txt"))error("B:/help.txt not found");
|
|
if(!argc){
|
|
MMPrintString("Enter help and the name of the command or function\r\nUse * for multicharacter wildcard or ? for single character wildcard\r\n");
|
|
} else {
|
|
int fnbr = FindFreeFileNbr();
|
|
char *buff=GetTempMemory(STRINGSIZE);
|
|
BasicFileOpen("B:/help.txt",fnbr, FA_READ);
|
|
int ListCnt = CurrentY/(FontTable[gui_font >> 4][1] * (gui_font & 0b1111)) + 2;
|
|
char *p=(char *)getCstring(argv[0]);
|
|
bool end=false;
|
|
while (!FileEOF(fnbr)) { // while waiting for the end of file
|
|
memset(buff,0,STRINGSIZE);
|
|
char *in=buff;
|
|
while(1){
|
|
if(FileEOF(fnbr)){end=true;break;}
|
|
char c = FileGetChar(fnbr);
|
|
if(c=='\n')break;
|
|
if(c=='\r')continue;
|
|
*in++=c;
|
|
}
|
|
if(end)break;
|
|
skipspace(p);
|
|
if(buff[0]=='~'){
|
|
if(pattern_matching(p,&buff[1],0,0)){
|
|
while(1){ //loop through all lines for the command
|
|
memset(buff,0,STRINGSIZE);
|
|
char *in=buff;
|
|
while(1){ //get this line
|
|
if(FileEOF(fnbr)){end=true;break;}
|
|
char c = FileGetChar(fnbr);
|
|
if(c=='\n')break;
|
|
if(c=='\r')continue;
|
|
*in++=c;
|
|
}
|
|
if(end)break;
|
|
if(buff[0]=='~'){ //now we need to rewind the file to check this line
|
|
ListNewLine(&ListCnt, false);
|
|
lfs_file_seek(&lfs, FileTable[fnbr].lfsptr, -(strlen(buff)+2), LFS_SEEK_CUR);
|
|
break;
|
|
} else {
|
|
ListCnt=printWrappedText(buff,Option.Width-1,ListCnt,false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
FileClose(fnbr);
|
|
}
|
|
}
|
|
void MIPS16 cmd_run(void){
|
|
do_run(cmdline,false);
|
|
}
|
|
|
|
void MIPS16 cmd_RunCMM2(void){
|
|
do_run(cmdline,true);
|
|
}
|
|
|
|
void MIPS16 cmd_continue(void) {
|
|
if(*cmdline == tokenFOR) {
|
|
if(g_forindex == 0) error("No FOR loop is in effect");
|
|
nextstmt = g_forstack[g_forindex - 1].nextptr;
|
|
return;
|
|
}
|
|
if(checkstring(cmdline, (unsigned char *)"DO")) {
|
|
if(g_doindex == 0) error("No DO loop is in effect");
|
|
nextstmt = g_dostack[g_doindex - 1].loopptr;
|
|
return;
|
|
}
|
|
// must be a normal CONTINUE
|
|
checkend(cmdline);
|
|
if(CurrentLinePtr) error("Invalid in a program");
|
|
if(ContinuePoint == NULL) error("Cannot continue");
|
|
// IgnorePIN = false;
|
|
nextstmt = ContinuePoint;
|
|
}
|
|
|
|
void MIPS16 cmd_new(void) {
|
|
closeframebuffer('A');
|
|
checkend(cmdline);
|
|
ClearProgram(true);
|
|
FlashLoad=0;
|
|
uSec(250000);
|
|
FlashWriteInit(PROGRAM_FLASH);
|
|
flash_range_erase(realflashpointer, MAX_PROG_SIZE);
|
|
FlashWriteByte(0); FlashWriteByte(0); FlashWriteByte(0); // terminate the program in flash
|
|
FlashWriteClose();
|
|
#ifdef PICOMITEVGA
|
|
int mode = DISPLAY_TYPE-SCREENMODE1+1;
|
|
setmode(mode, true);
|
|
#endif
|
|
memset(inpbuf,0,STRINGSIZE);
|
|
longjmp(mark, 1); // jump back to the input prompt
|
|
}
|
|
|
|
|
|
void MIPS16 cmd_erase(void) {
|
|
int i,j,k, len;
|
|
char p[MAXVARLEN + 1], *s, *x;
|
|
|
|
getargs(&cmdline, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); // getargs macro must be the first executable stmt in a block
|
|
if((argc & 0x01) == 0) error("Argument count");
|
|
for(i = 0; i < argc; i += 2) {
|
|
strcpy((char *)p, (char *)argv[i]);
|
|
if(*argv[i] & 0x80)error("You can't erase an in-built function");
|
|
while(!isnamechar(p[strlen(p) - 1])) p[strlen(p) - 1] = 0;
|
|
makeupper((unsigned char *)p);
|
|
for(j = MAXVARS/2; j < MAXVARS; j++) {
|
|
s = p; x = (char *)g_vartbl[j].name; len = strlen(p);
|
|
while(len > 0 && *s == *x) { // compare the variable to the name that we have
|
|
len--; s++; x++;
|
|
}
|
|
if(!(len == 0 && (*x == 0 || strlen(p) == MAXVARLEN))) continue;
|
|
// found the variable
|
|
if(((g_vartbl[j].type & T_STR) || g_vartbl[j].dims[0] != 0) && !(g_vartbl[j].type & T_PTR)) {
|
|
FreeMemory(g_vartbl[j].val.s); // free any memory (if allocated)
|
|
g_vartbl[j].val.s=NULL;
|
|
}
|
|
k=j+1;
|
|
if(k==MAXVARS)k=MAXVARS/2;
|
|
if(g_vartbl[k].type){
|
|
g_vartbl[j].name[0]='~';
|
|
g_vartbl[j].type=T_BLOCKED;
|
|
} else {
|
|
g_vartbl[j].name[0]=0;
|
|
g_vartbl[j].type=T_NOTYPE;
|
|
}
|
|
g_vartbl[j].dims[0] = 0; // and again
|
|
g_vartbl[j].level = 0;
|
|
g_Globalvarcnt--;
|
|
break;
|
|
}
|
|
if(j == MAXVARS) error("Cannot find $", p);
|
|
}
|
|
}
|
|
void MIPS16 cmd_clear(void) {
|
|
checkend(cmdline);
|
|
if(g_LocalIndex)error("Invalid in a subroutine");
|
|
ClearVars(0,true);
|
|
}
|
|
|
|
|
|
void cmd_goto(void) {
|
|
if(isnamestart(*cmdline))
|
|
nextstmt = findlabel(cmdline); // must be a label
|
|
else
|
|
nextstmt = findline(getinteger(cmdline), true); // try for a line number
|
|
CurrentLinePtr = nextstmt;
|
|
}
|
|
|
|
|
|
|
|
#ifdef PICOMITEWEB
|
|
#ifdef rp2350
|
|
void MIPS16 __not_in_flash_func(cmd_if)(void) {
|
|
#else
|
|
void cmd_if(void) {
|
|
#endif
|
|
#else
|
|
#ifndef rp2350
|
|
#ifdef PICOMITEVGA
|
|
void cmd_if(void) {
|
|
#else
|
|
void MIPS16 __not_in_flash_func(cmd_if)(void) {
|
|
#endif
|
|
#else
|
|
void MIPS16 __not_in_flash_func(cmd_if)(void) {
|
|
#endif
|
|
#endif
|
|
int r, i, testgoto, testelseif;
|
|
unsigned char ss[3]; // this will be used to split up the argument line
|
|
unsigned char *p, *tp;
|
|
unsigned char *rp = NULL;
|
|
|
|
ss[0] = tokenTHEN;
|
|
ss[1] = tokenELSE;
|
|
ss[2] = 0;
|
|
|
|
testgoto = false;
|
|
testelseif = false;
|
|
|
|
retest_an_if:
|
|
{ // start a new block
|
|
getargs(&cmdline, 20, ss); // getargs macro must be the first executable stmt in a block
|
|
|
|
if(testelseif && argc > 2) error("Unexpected text");
|
|
|
|
// if there is no THEN token retry the test with a GOTO. If that fails flag an error
|
|
if(argc < 2 || *argv[1] != ss[0]) {
|
|
if(testgoto) error("IF without THEN");
|
|
ss[0] = tokenGOTO;
|
|
testgoto = true;
|
|
goto retest_an_if;
|
|
}
|
|
|
|
|
|
// allow for IF statements embedded inside this IF
|
|
if (argc >= 3 && commandtbl_decode(argv[2]) == cmdIF) argc = 3; // this is IF xx=yy THEN IF ... so we want to evaluate only the first 3
|
|
if (argc >= 5 && commandtbl_decode(argv[4]) == cmdIF) argc = 5; // this is IF xx=yy THEN cmd ELSE IF ... so we want to evaluate only the first 5
|
|
|
|
if(argc == 4 || (argc == 5 && *argv[3] != ss[1])) error("Syntax");
|
|
|
|
r = (getnumber(argv[0]) != 0); // evaluate the expression controlling the if statement
|
|
|
|
if(r) {
|
|
// the test returned TRUE
|
|
// first check if it is a multiline IF (ie, only 2 args)
|
|
if(argc == 2) {
|
|
// if multiline do nothing, control will fall through to the next line (which is what we want to execute next)
|
|
;
|
|
}
|
|
else {
|
|
// This is a standard single line IF statement
|
|
// Because the test was TRUE we are just interested in the THEN cmd stage.
|
|
if(*argv[1] == tokenGOTO) {
|
|
cmdline = argv[2];
|
|
cmd_goto();
|
|
return;
|
|
} else if(isdigit(*argv[2])) {
|
|
nextstmt = findline(getinteger(argv[2]), true);
|
|
} else {
|
|
if(argc == 5) {
|
|
// this is a full IF THEN ELSE and the statement we want to execute is between the THEN & ELSE
|
|
// this is handled by a special routine
|
|
execute_one_command(argv[2]);
|
|
} else {
|
|
// easy - there is no ELSE clause so just point the next statement pointer to the byte after the THEN token
|
|
for(p = cmdline; *p && *p != ss[0]; p++); // search for the token
|
|
nextstmt = p + 1; // and point to the byte after
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// the test returned FALSE so we are just interested in the ELSE stage (if present)
|
|
// first check if it is a multiline IF (ie, only 2 args)
|
|
if(argc == 2) {
|
|
// search for the next ELSE, or ENDIF and pass control to the following line
|
|
// if an ELSEIF is found re execute this function to evaluate the condition following the ELSEIF
|
|
i = 1; p = nextstmt;
|
|
while(1) {
|
|
p = GetNextCommand(p, &rp, (unsigned char *)"No matching ENDIF");
|
|
CommandToken tkn=commandtbl_decode(p);
|
|
if(tkn == cmdtoken) {
|
|
// found a nested IF command, we now need to determine if it is a single or multiline IF
|
|
// search for a THEN, then check if only white space follows. If so, it is multiline.
|
|
tp = p + sizeof(CommandToken);
|
|
while(*tp && *tp != ss[0]) tp++;
|
|
if(*tp) tp++; // step over the THEN
|
|
skipspace(tp);
|
|
if(*tp == 0 || *tp == '\'') // yes, only whitespace follows
|
|
i++; // count it as a nested IF
|
|
else // no, it is a single line IF
|
|
skipelement(p); // skip to the end so that we avoid an ELSE
|
|
continue;
|
|
}
|
|
|
|
if(tkn == cmdELSE && i == 1) {
|
|
// found an ELSE at the same level as this IF. Step over it and continue with the statement after it
|
|
skipelement(p);
|
|
nextstmt = p;
|
|
break;
|
|
}
|
|
|
|
if((tkn == cmdELSEIF || tkn==cmdELSE_IF) && i == 1) {
|
|
// we have found an ELSEIF statement at the same level as our IF statement
|
|
// setup the environment to make this function evaluate the test following ELSEIF and jump back
|
|
// to the start of the function. This is not very clean (it uses the dreaded goto for a start) but it works
|
|
p+=sizeof(CommandToken); // step over the token
|
|
skipspace(p);
|
|
CurrentLinePtr = rp;
|
|
if(*p == 0) error("Syntax"); // there must be a test after the elseif
|
|
cmdline = p;
|
|
skipelement(p);
|
|
nextstmt = p;
|
|
testgoto = false;
|
|
testelseif = true;
|
|
goto retest_an_if;
|
|
}
|
|
|
|
if(tkn == cmdENDIF || tkn==cmdEND_IF) i--; // found an ENDIF so decrement our nested counter
|
|
if(i == 0) {
|
|
// found our matching ENDIF stmt. Step over it and continue with the statement after it
|
|
skipelement(p);
|
|
nextstmt = p;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// this must be a single line IF statement
|
|
// check if there is an ELSE on the same line
|
|
if(argc == 5) {
|
|
// there is an ELSE command
|
|
if(isdigit(*argv[4]))
|
|
// and it is just a number, so get it and find the line
|
|
nextstmt = findline(getinteger(argv[4]), true);
|
|
else {
|
|
/* // there is a statement after the ELSE clause so just point to it (the byte after the ELSE token)
|
|
for(p = cmdline; *p && *p != ss[1]; p++); // search for the token
|
|
nextstmt = p + 1; // and point to the byte after
|
|
*/
|
|
// IF <condition> THEN <statement1> ELSE <statement2>
|
|
// Find and read the THEN function token.
|
|
for(p = cmdline; *p && *p != ss[0]; p++){}
|
|
// Skip the command that <statement1> must start with.
|
|
p++;
|
|
skipspace(p);
|
|
p += sizeof(CommandToken);
|
|
// Find and read the ELSE function token.
|
|
for(; *p && *p != ss[1]; p++);
|
|
nextstmt = p+1; // The statement after the ELSE token.
|
|
}
|
|
} else {
|
|
// no ELSE on a single line IF statement, so just continue with the next statement
|
|
skipline(cmdline);
|
|
nextstmt = cmdline;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#ifdef PICOMITEWEB
|
|
#ifdef rp2350
|
|
void MIPS16 __not_in_flash_func(cmd_else)(void) {
|
|
#else
|
|
void cmd_else(void) {
|
|
#endif
|
|
#else
|
|
#ifndef rp2350
|
|
#ifdef PICOMITEVGA
|
|
void cmd_else(void) {
|
|
#else
|
|
void MIPS16 __not_in_flash_func(cmd_else)(void) {
|
|
#endif
|
|
#else
|
|
void MIPS16 __not_in_flash_func(cmd_else)(void) {
|
|
#endif
|
|
#endif
|
|
int i;
|
|
unsigned char *p, *tp;
|
|
|
|
// search for the next ENDIF and pass control to the following line
|
|
i = 1; p = nextstmt;
|
|
|
|
if(cmdtoken == cmdELSE) checkend(cmdline);
|
|
|
|
while(1) {
|
|
p = GetNextCommand(p, NULL, (unsigned char *)"No matching ENDIF");
|
|
CommandToken tkn=commandtbl_decode(p);
|
|
if(tkn == cmdIF) {
|
|
// found a nested IF command, we now need to determine if it is a single or multiline IF
|
|
// search for a THEN, then check if only white space follows. If so, it is multiline.
|
|
tp = p + sizeof(CommandToken);
|
|
while(*tp && *tp != tokenTHEN) tp++;
|
|
if(*tp) tp++; // step over the THEN
|
|
skipspace(tp);
|
|
if(*tp == 0 || *tp == '\'') // yes, only whitespace follows
|
|
i++; // count it as a nested IF
|
|
}
|
|
if(tkn == cmdENDIF || tkn==cmdEND_IF) i--; // found an ENDIF so decrement our nested counter
|
|
if(i == 0) break; // found our matching ENDIF stmt
|
|
}
|
|
// found a matching ENDIF. Step over it and continue with the statement after it
|
|
skipelement(p);
|
|
nextstmt = p;
|
|
}
|
|
|
|
|
|
|
|
void do_end(bool ecmd) {
|
|
#ifdef PICOMITE
|
|
if(mergerunning){
|
|
multicore_fifo_push_blocking(0xFF);
|
|
mergerunning=false;
|
|
busy_wait_ms(100);
|
|
}
|
|
#endif
|
|
if(Option.SerialConsole)while(ConsoleTxBufHead!=ConsoleTxBufTail)routinechecks();
|
|
fflush(stdout);
|
|
if(ecmd){
|
|
getargs(&cmdline,1,(unsigned char *)",");
|
|
if(argc==1){
|
|
if(FindSubFun((unsigned char *)"MM.END", 0) >= 0 && checkstring(argv[0],(unsigned char *)"NOEND")==NULL) {
|
|
ExecuteProgram((unsigned char *)"MM.END\0");
|
|
if(Option.SerialConsole)while(ConsoleTxBufHead!=ConsoleTxBufTail)routinechecks();
|
|
fflush(stdout);
|
|
memset(inpbuf,0,STRINGSIZE);
|
|
} else {
|
|
unsigned char *cmd_args = (unsigned char *)"";
|
|
cmd_args = getCstring(argv[0]);
|
|
void *ptr = findvar((unsigned char *)"MM.ENDLINE$", T_STR| V_NOFIND_NULL);
|
|
if(ptr==NULL)ptr = findvar((unsigned char *)"MM.ENDLINE$", V_FIND |V_DIM_VAR);
|
|
strcpy(ptr, (char *)cmd_args ); // *** THW 16/4/23
|
|
CtoM(ptr);
|
|
}
|
|
} else if(FindSubFun((unsigned char *)"MM.END", 0) >= 0) {
|
|
ExecuteProgram((unsigned char *)"MM.END\0");
|
|
if(Option.SerialConsole)while(ConsoleTxBufHead!=ConsoleTxBufTail)routinechecks();
|
|
fflush(stdout);
|
|
memset(inpbuf,0,STRINGSIZE);
|
|
}
|
|
}
|
|
if(!(MMerrno == 16))hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
|
|
irq_set_enabled(DMA_IRQ_1, false);
|
|
dma_hw->abort = ((1u << dma_rx_chan2) | (1u << dma_rx_chan));
|
|
if(dma_channel_is_busy(dma_rx_chan))dma_channel_abort(dma_rx_chan);
|
|
if(dma_channel_is_busy(dma_rx_chan2))dma_channel_abort(dma_rx_chan2);
|
|
// dma_channel_cleanup(dma_rx_chan);
|
|
// dma_channel_cleanup(dma_rx_chan2);
|
|
dma_hw->abort = ((1u << dma_tx_chan2) | (1u << dma_tx_chan));
|
|
if(dma_channel_is_busy(dma_tx_chan))dma_channel_abort(dma_tx_chan);
|
|
if(dma_channel_is_busy(dma_tx_chan2))dma_channel_abort(dma_tx_chan2);
|
|
// dma_channel_cleanup(dma_tx_chan);
|
|
// dma_channel_cleanup(dma_tx_chan2);
|
|
dma_hw->abort = ((1u << ADC_dma_chan2) | (1u << ADC_dma_chan));
|
|
if(dma_channel_is_busy(ADC_dma_chan))dma_channel_abort(ADC_dma_chan);
|
|
if(dma_channel_is_busy(ADC_dma_chan2))dma_channel_abort(ADC_dma_chan2);
|
|
// dma_channel_cleanup(ADC_dma_chan);
|
|
// dma_channel_cleanup(ADC_dma_chan2);
|
|
for(int i=0; i< NBRSETTICKS;i++){
|
|
TickPeriod[i]=0;
|
|
TickTimer[i]=0;
|
|
TickInt[i]=NULL;
|
|
TickActive[i]=0;
|
|
}
|
|
InterruptUsed=0;
|
|
InterruptReturn = NULL ;
|
|
memset(inpbuf,0,STRINGSIZE);
|
|
CloseAudio(1);
|
|
CloseAllFiles();
|
|
ADCDualBuffering=0;
|
|
WatchdogSet = false;
|
|
WDTimer = 0;
|
|
hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
|
|
_excep_code=0;
|
|
dmarunning = false;
|
|
multi=false;
|
|
WAVInterrupt = NULL;
|
|
WAVcomplete = 0;
|
|
if(g_myrand)FreeMemory((void *)g_myrand);
|
|
g_myrand=NULL;
|
|
OptionConsole=3;
|
|
#ifdef PICOMITEVGA
|
|
int mode = DISPLAY_TYPE-SCREENMODE1+1;
|
|
setmode(mode,false);
|
|
#endif
|
|
SSPrintString("\033[?25h"); //in case application has turned the cursor off
|
|
SSPrintString("\033[97;40m");
|
|
#ifdef PICOMITEWEB
|
|
close_tcpclient();
|
|
#endif
|
|
#ifndef USBKEYBOARD
|
|
if(mouse0==false && Option.MOUSE_CLOCK)initMouse0(0); //see if there is a mouse to initialise
|
|
#endif
|
|
}
|
|
void cmd_end(void) {
|
|
do_end(true);
|
|
longjmp(mark, 1); // jump back to the input prompt
|
|
}
|
|
extern unsigned int mmap[HEAP_MEMORY_SIZE/ PAGESIZE / PAGESPERWORD];
|
|
extern unsigned int psmap[7*1024*1024/ PAGESIZE / PAGESPERWORD];
|
|
extern struct s_hash g_hashlist[MAXVARS/2];
|
|
extern int g_hashlistpointer;
|
|
extern short g_StrTmpIndex;
|
|
extern bool g_TempMemoryIsChanged;
|
|
extern volatile char *g_StrTmp[MAXTEMPSTRINGS]; // used to track temporary string space on the heap
|
|
extern volatile char g_StrTmpLocalIndex[MAXTEMPSTRINGS]; // used to track the g_LocalIndex for each temporary string space on the heap
|
|
void SaveContext(void){
|
|
CloseAudio(1);
|
|
#if defined(rp2350) && !defined(PICOMITEWEB)
|
|
if(PSRAMsize){
|
|
ClearTempMemory();
|
|
uint8_t *p=(uint8_t *)PSRAMbase+PSRAMsize;
|
|
memcpy(p, &g_StrTmpIndex, sizeof(g_StrTmpIndex));
|
|
p+=sizeof(g_StrTmpIndex);
|
|
memcpy(p, &g_TempMemoryIsChanged, sizeof(g_TempMemoryIsChanged));
|
|
p+=sizeof(g_TempMemoryIsChanged);
|
|
memcpy(p, (void *)g_StrTmp, sizeof(g_StrTmp));
|
|
p+=sizeof(g_StrTmp);
|
|
memcpy(p, (void *)g_StrTmpLocalIndex, sizeof(g_StrTmpLocalIndex));
|
|
p+=sizeof(g_StrTmpLocalIndex);
|
|
memcpy(p, &g_LocalIndex, sizeof(g_LocalIndex));
|
|
p+=sizeof(g_LocalIndex);
|
|
memcpy(p, &g_OptionBase, sizeof(g_OptionBase));
|
|
p+=sizeof(g_OptionBase);
|
|
memcpy(p, &g_DimUsed, sizeof(g_DimUsed));
|
|
p+=sizeof(g_DimUsed);
|
|
memcpy(p, &g_varcnt, sizeof(g_varcnt));
|
|
p+=sizeof(g_varcnt);
|
|
memcpy(p, &g_Globalvarcnt, sizeof(g_Globalvarcnt));
|
|
p+=sizeof(g_Globalvarcnt);
|
|
memcpy(p, &g_Localvarcnt, sizeof(g_Localvarcnt));
|
|
p+=sizeof(g_Localvarcnt);
|
|
memcpy(p, &g_hashlistpointer, sizeof(g_hashlistpointer));
|
|
p+=sizeof(g_hashlistpointer);
|
|
memcpy(p, &g_forindex, sizeof(g_forindex));
|
|
p+=sizeof(g_forindex);
|
|
memcpy(p, &g_doindex, sizeof(g_doindex));
|
|
p+=sizeof(g_doindex);
|
|
memcpy(p, g_forstack, sizeof(struct s_forstack)*MAXFORLOOPS);
|
|
p+=sizeof(struct s_forstack)*MAXFORLOOPS;
|
|
memcpy(p, g_dostack, sizeof(struct s_dostack)*MAXDOLOOPS);
|
|
p+=sizeof(struct s_dostack)*MAXDOLOOPS;
|
|
memcpy(p, g_vartbl, sizeof(struct s_vartbl)*MAXVARS);
|
|
p+=sizeof(struct s_vartbl)*MAXVARS;
|
|
memcpy(p, g_hashlist, sizeof(struct s_hash)*MAXVARS/2);
|
|
p+=sizeof(struct s_hash)*MAXVARS/2;
|
|
memcpy(p, MMHeap, heap_memory_size+256);
|
|
p+=heap_memory_size+256;
|
|
memcpy(p, mmap, sizeof(mmap));
|
|
p+=sizeof(mmap);
|
|
memcpy(p, psmap, sizeof(psmap));
|
|
p+=sizeof(psmap);
|
|
} else {
|
|
#endif
|
|
lfs_file_t lfs_file;
|
|
if(ExistsFile("A:/.vars")){
|
|
lfs_remove(&lfs, "/.vars");
|
|
}
|
|
int sizeneeded= sizeof(g_StrTmpIndex)+ sizeof(g_TempMemoryIsChanged)+sizeof(g_StrTmp)+sizeof(g_StrTmpLocalIndex)+sizeof(g_Localvarcnt)+
|
|
sizeof(g_LocalIndex)+sizeof(g_OptionBase)+sizeof(g_DimUsed)+sizeof(g_varcnt)+sizeof(g_Globalvarcnt)+
|
|
sizeof(g_hashlistpointer)+sizeof(g_forindex)+sizeof(g_doindex)+sizeof(struct s_forstack)*MAXFORLOOPS+sizeof(struct s_dostack)*MAXDOLOOPS+
|
|
sizeof(struct s_vartbl)*MAXVARS+sizeof(struct s_hash)*MAXVARS/2+heap_memory_size+256+sizeof(mmap);
|
|
if(sizeneeded>=Option.FlashSize-(Option.modbuff ? 1024*Option.modbuffsize : 0)-RoundUpK4(TOP_OF_SYSTEM_FLASH)-lfs_fs_size(&lfs)*4096)error("Not enough free space on A: drive: % needed",sizeneeded);
|
|
lfs_file_open(&lfs, &lfs_file, ".vars", LFS_O_RDWR | LFS_O_CREAT);;
|
|
int dt=get_fattime();
|
|
ClearTempMemory();
|
|
lfs_setattr(&lfs, ".vars", 'A', &dt, 4);
|
|
lfs_file_write(&lfs, &lfs_file, &g_StrTmpIndex, sizeof(g_StrTmpIndex));
|
|
lfs_file_write(&lfs, &lfs_file, &g_TempMemoryIsChanged, sizeof(g_TempMemoryIsChanged));
|
|
lfs_file_write(&lfs, &lfs_file, (void *)g_StrTmp, sizeof(g_StrTmp));
|
|
lfs_file_write(&lfs, &lfs_file, (void *)g_StrTmpLocalIndex, sizeof(g_StrTmpLocalIndex));
|
|
lfs_file_write(&lfs, &lfs_file, &g_LocalIndex, sizeof(g_LocalIndex));
|
|
lfs_file_write(&lfs, &lfs_file, &g_OptionBase, sizeof(g_OptionBase));
|
|
lfs_file_write(&lfs, &lfs_file, &g_DimUsed, sizeof(g_DimUsed));
|
|
lfs_file_write(&lfs, &lfs_file, &g_varcnt, sizeof(g_varcnt));
|
|
lfs_file_write(&lfs, &lfs_file, &g_Globalvarcnt, sizeof(g_Globalvarcnt));
|
|
lfs_file_write(&lfs, &lfs_file, &g_Localvarcnt, sizeof(g_Globalvarcnt));
|
|
lfs_file_write(&lfs, &lfs_file, &g_hashlistpointer, sizeof(g_hashlistpointer));
|
|
lfs_file_write(&lfs, &lfs_file, &g_forindex, sizeof(g_forindex));
|
|
lfs_file_write(&lfs, &lfs_file, &g_doindex, sizeof(g_doindex));
|
|
lfs_file_write(&lfs, &lfs_file, g_forstack, sizeof(struct s_forstack)*MAXFORLOOPS);
|
|
lfs_file_write(&lfs, &lfs_file, g_dostack, sizeof(struct s_dostack)*MAXDOLOOPS);
|
|
lfs_file_write(&lfs, &lfs_file, g_vartbl, sizeof(struct s_vartbl)*MAXVARS);
|
|
lfs_file_write(&lfs, &lfs_file, g_hashlist, sizeof(struct s_hash)*MAXVARS/2);
|
|
lfs_file_write(&lfs, &lfs_file, MMHeap, heap_memory_size+256);
|
|
lfs_file_write(&lfs, &lfs_file, mmap, sizeof(mmap));
|
|
lfs_file_close(&lfs, &lfs_file);
|
|
#if defined(rp2350) && !defined(PICOMITEWEB)
|
|
}
|
|
#endif
|
|
}
|
|
void RestoreContext(bool keep){
|
|
CloseAudio(1);
|
|
#if defined(rp2350) && !defined(PICOMITEWEB)
|
|
if(PSRAMsize){
|
|
uint8_t *p=(uint8_t *)PSRAMbase+PSRAMsize;
|
|
memcpy(&g_StrTmpIndex, p, sizeof(g_StrTmpIndex));
|
|
p+=sizeof(g_StrTmpIndex);
|
|
memcpy(&g_TempMemoryIsChanged, p, sizeof(g_TempMemoryIsChanged));
|
|
p+=sizeof(g_TempMemoryIsChanged);
|
|
memcpy((void *)g_StrTmp, p, sizeof(g_StrTmp));
|
|
p+=sizeof(g_StrTmp);
|
|
memcpy((void *)g_StrTmpLocalIndex, p, sizeof(g_StrTmpLocalIndex));
|
|
p+=sizeof(g_StrTmpLocalIndex);
|
|
memcpy(&g_LocalIndex, p, sizeof(g_LocalIndex));
|
|
p+=sizeof(g_LocalIndex);
|
|
memcpy(&g_OptionBase, p, sizeof(g_OptionBase));
|
|
p+=sizeof(g_OptionBase);
|
|
memcpy(&g_DimUsed, p, sizeof(g_DimUsed));
|
|
p+=sizeof(g_DimUsed);
|
|
memcpy(&g_varcnt, p, sizeof(g_varcnt));
|
|
p+=sizeof(g_varcnt);
|
|
memcpy(&g_Globalvarcnt, p, sizeof(g_Globalvarcnt));
|
|
p+=sizeof(g_Globalvarcnt);
|
|
memcpy(&g_Localvarcnt, p, sizeof(g_Localvarcnt));
|
|
p+=sizeof(g_Localvarcnt);
|
|
memcpy(&g_hashlistpointer, p, sizeof(g_hashlistpointer));
|
|
p+=sizeof(g_hashlistpointer);
|
|
memcpy(&g_forindex, p, sizeof(g_forindex));
|
|
p+=sizeof(g_forindex);
|
|
memcpy(&g_doindex, p, sizeof(g_doindex));
|
|
p+=sizeof(g_doindex);
|
|
memcpy(g_forstack, p, sizeof(struct s_forstack)*MAXFORLOOPS);
|
|
p+=sizeof(struct s_forstack)*MAXFORLOOPS;
|
|
memcpy(g_dostack, p, sizeof(struct s_dostack)*MAXDOLOOPS);
|
|
p+=sizeof(struct s_dostack)*MAXDOLOOPS;
|
|
memcpy(g_vartbl, p, sizeof(struct s_vartbl)*MAXVARS);
|
|
p+=sizeof(struct s_vartbl)*MAXVARS;
|
|
memcpy(g_hashlist, p, sizeof(struct s_hash)*MAXVARS/2);
|
|
p+=sizeof(struct s_hash)*MAXVARS/2;
|
|
memcpy(MMHeap, p, heap_memory_size+256);
|
|
p+=heap_memory_size+256;
|
|
memcpy(mmap, p, sizeof(mmap));
|
|
p+=sizeof(mmap);
|
|
memcpy(psmap, p, sizeof(psmap));
|
|
p+=sizeof(psmap);
|
|
} else {
|
|
#endif
|
|
lfs_file_t lfs_file;
|
|
if(!ExistsFile("A:/.vars"))error("Internal error");
|
|
lfs_file_open(&lfs, &lfs_file, "/.vars", LFS_O_RDONLY);
|
|
lfs_file_read(&lfs, &lfs_file, &g_StrTmpIndex, sizeof(g_StrTmpIndex));
|
|
lfs_file_read(&lfs, &lfs_file, &g_TempMemoryIsChanged, sizeof(g_TempMemoryIsChanged));
|
|
lfs_file_read(&lfs, &lfs_file, (void *)g_StrTmp, sizeof(g_StrTmp));
|
|
lfs_file_read(&lfs, &lfs_file, (void *)g_StrTmpLocalIndex, sizeof(g_StrTmpLocalIndex));
|
|
lfs_file_read(&lfs, &lfs_file, &g_LocalIndex, sizeof(g_LocalIndex));
|
|
lfs_file_read(&lfs, &lfs_file, &g_OptionBase, sizeof(g_OptionBase));
|
|
lfs_file_read(&lfs, &lfs_file, &g_DimUsed, sizeof(g_DimUsed));
|
|
lfs_file_read(&lfs, &lfs_file, &g_varcnt, sizeof(g_varcnt));
|
|
lfs_file_read(&lfs, &lfs_file, &g_Globalvarcnt, sizeof(g_Globalvarcnt));
|
|
lfs_file_read(&lfs, &lfs_file, &g_Localvarcnt, sizeof(g_Globalvarcnt));
|
|
lfs_file_read(&lfs, &lfs_file, &g_hashlistpointer, sizeof(g_hashlistpointer));
|
|
lfs_file_read(&lfs, &lfs_file, &g_forindex, sizeof(g_forindex));
|
|
lfs_file_read(&lfs, &lfs_file, &g_doindex, sizeof(g_doindex));
|
|
lfs_file_read(&lfs, &lfs_file, g_forstack, sizeof(struct s_forstack)*MAXFORLOOPS);
|
|
lfs_file_read(&lfs, &lfs_file, g_dostack, sizeof(struct s_dostack)*MAXDOLOOPS);
|
|
lfs_file_read(&lfs, &lfs_file, g_vartbl, sizeof(struct s_vartbl)*MAXVARS);
|
|
lfs_file_read(&lfs, &lfs_file, g_hashlist, sizeof(struct s_hash)*MAXVARS/2);
|
|
lfs_file_read(&lfs, &lfs_file, MMHeap, heap_memory_size+256);
|
|
lfs_file_read(&lfs, &lfs_file, mmap, sizeof(mmap));
|
|
lfs_file_close(&lfs, &lfs_file);
|
|
if(!keep)lfs_remove(&lfs, "/.vars");
|
|
#if defined(rp2350) && !defined(PICOMITEWEB)
|
|
}
|
|
#endif
|
|
}
|
|
extern void chdir(char *p);
|
|
void MIPS16 do_chain(unsigned char *cmdline){
|
|
unsigned char *filename = (unsigned char *)"", *cmd_args = (unsigned char *)"";
|
|
unsigned char *cmdbuf=GetMemory(256);
|
|
memcpy(cmdbuf,cmdline,STRINGSIZE);
|
|
getargs(&cmdbuf, 3, (unsigned char *)",");
|
|
switch (argc) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
filename = getCstring(argv[0]);
|
|
break;
|
|
case 2:
|
|
cmd_args = getCstring(argv[1]);
|
|
break;
|
|
default:
|
|
filename = getCstring(argv[0]);
|
|
if(*argv[2])cmd_args = getCstring(argv[2]);
|
|
break;
|
|
}
|
|
|
|
// The memory allocated by getCstring() is not preserved across
|
|
// a call to FileLoadProgram() so we need to cache 'filename' and
|
|
// 'cmd_args' on the stack.
|
|
unsigned char buf[MAXSTRLEN + 1];
|
|
if (snprintf((char *)buf, MAXSTRLEN + 1, "\"%s\",%s", filename, cmd_args) > MAXSTRLEN) {
|
|
error("RUN command line too long");
|
|
}
|
|
FreeMemory(cmdbuf);
|
|
unsigned char *pcmd_args = buf + strlen((char *)filename) + 3; // *** THW 16/4/23
|
|
*cmdline=0;
|
|
do_end(false);
|
|
SaveContext();
|
|
ClearVars(0,false);
|
|
InitHeap(false);
|
|
if (*buf && !FileLoadProgram(buf, true)) return;
|
|
ClearRuntime(false);
|
|
PrepareProgram(true);
|
|
RestoreContext(false);
|
|
if(Option.DISPLAY_CONSOLE && (SPIREAD || Option.NoScroll)){ClearScreen(gui_bcolour);CurrentX=0;CurrentY=0;}
|
|
// Create a global constant MM.CMDLINE$ containing 'cmd_args'.
|
|
// void *ptr = findvar((unsigned char *)"MM.CMDLINE$", V_NOFIND_ERR);
|
|
CtoM(pcmd_args);
|
|
// memcpy(cmdlinebuff, pcmd_args, *pcmd_args + 1); // *** THW 16/4/23
|
|
Mstrcpy(cmdlinebuff, pcmd_args);
|
|
IgnorePIN = false;
|
|
if(Option.LIBRARY_FLASH_SIZE == MAX_PROG_SIZE) ExecuteProgram(LibMemory ); // run anything that might be in the library
|
|
if(*ProgMemory != T_NEWLINE) return; // no program to run
|
|
#ifdef PICOMITEWEB
|
|
cleanserver();
|
|
#endif
|
|
#ifndef USBKEYBOARD
|
|
if(mouse0==false && Option.MOUSE_CLOCK)initMouse0(0); //see if there is a mouse to initialise
|
|
#endif
|
|
nextstmt = ProgMemory;
|
|
}
|
|
void cmd_chain(void){
|
|
do_chain(cmdline);
|
|
}
|
|
|
|
void cmd_select(void) {
|
|
int i, type;
|
|
unsigned char *p, *rp = NULL, *SaveCurrentLinePtr;
|
|
void *v;
|
|
MMFLOAT f = 0;
|
|
long long int i64 = 0;
|
|
unsigned char s[STRINGSIZE];
|
|
|
|
// these are the tokens that we will be searching for
|
|
// they are cached the first time this command is called
|
|
|
|
type = T_NOTYPE;
|
|
v = DoExpression(cmdline, &type); // evaluate the select case value
|
|
type = TypeMask(type);
|
|
if(type & T_NBR) f = *(MMFLOAT *)v;
|
|
if(type & T_INT) i64 = *(long long int *)v;
|
|
if(type & T_STR) Mstrcpy((unsigned char *)s, (unsigned char *)v);
|
|
|
|
// now search through the program looking for a matching CASE statement
|
|
// i tracks the nesting level of any nested SELECT CASE commands
|
|
SaveCurrentLinePtr = CurrentLinePtr; // save where we are because we will have to fake CurrentLinePtr to get errors reported correctly
|
|
i = 1; p = nextstmt;
|
|
while(1) {
|
|
p = GetNextCommand(p, &rp, (unsigned char *)"No matching END SELECT");
|
|
CommandToken tkn=commandtbl_decode(p);
|
|
|
|
if(tkn == cmdSELECT_CASE) i++; // found a nested SELECT CASE command, increase the nested count and carry on searching
|
|
// is this a CASE stmt at the same level as this SELECT CASE.
|
|
if(tkn == cmdCASE && i == 1) {
|
|
int t;
|
|
MMFLOAT ft, ftt;
|
|
long long int i64t, i64tt;
|
|
unsigned char *st, *stt;
|
|
|
|
CurrentLinePtr = rp; // and report errors at the line we are on
|
|
p++; //step past rest of command token
|
|
// loop through the comparison elements on the CASE line. Each element is separated by a comma
|
|
do {
|
|
p++;
|
|
skipspace(p);
|
|
t = type;
|
|
// check for CASE IS, eg CASE IS > 5 -or- CASE > 5 and process it if it is
|
|
// an operator can be >, <>, etc but it can also be a prefix + or - so we must not catch them
|
|
if((SaveCurrentLinePtr = checkstring(p, (unsigned char *)"IS")) || ((tokentype(*p) & T_OPER) && !(*p == GetTokenValue((unsigned char *)"+") || *p == GetTokenValue((unsigned char *)"-")))) {
|
|
int o;
|
|
if(SaveCurrentLinePtr) p += 2;
|
|
skipspace(p);
|
|
if(tokentype(*p) & T_OPER)
|
|
o = *p++ - C_BASETOKEN; // get the operator
|
|
else
|
|
error("Syntax");
|
|
if(type & T_NBR) ft = f;
|
|
if(type & T_INT) i64t = i64;
|
|
if(type & T_STR) st = s;
|
|
while(o != E_END) p = doexpr(p, &ft, &i64t, &st, &o, &t); // get the right hand side of the expression and evaluate the operator in o
|
|
if(!(t & T_INT)) error("Syntax"); // comparisons must always return an integer
|
|
if(i64t) { // evaluates to true
|
|
skipelement(p);
|
|
nextstmt = p;
|
|
CurrentLinePtr = SaveCurrentLinePtr;
|
|
return; // if we have a match just return to the interpreter and let it execute the code
|
|
} else { // evaluates to false
|
|
skipspace(p);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// it must be either a single value (eg, "foo") or a range (eg, "foo" TO "zoo")
|
|
// evaluate the first value
|
|
p = evaluate(p, &ft, &i64t, &st, &t, true);
|
|
skipspace(p);
|
|
if(*p == tokenTO) { // is there is a TO keyword?
|
|
p++;
|
|
t = type;
|
|
p = evaluate(p, &ftt, &i64tt, &stt, &t, false); // evaluate the right hand side of the TO expression
|
|
if(((type & T_NBR) && f >= ft && f <= ftt) || ((type & T_INT) && i64 >= i64t && i64 <= i64tt) || (((type & T_STR) && Mstrcmp(s, st) >= 0) && (Mstrcmp(s, stt) <= 0))) {
|
|
skipelement(p);
|
|
nextstmt = p;
|
|
CurrentLinePtr = SaveCurrentLinePtr;
|
|
return; // if we have a match just return to the interpreter and let it execute the code
|
|
} else {
|
|
skipspace(p);
|
|
continue; // otherwise continue searching
|
|
}
|
|
}
|
|
|
|
// if we got to here the element must be just a single match. So make the test
|
|
if(((type & T_NBR) && f == ft) || ((type & T_INT) && i64 == i64t) || ((type & T_STR) && Mstrcmp(s, st) == 0)) {
|
|
skipelement(p);
|
|
nextstmt = p;
|
|
CurrentLinePtr = SaveCurrentLinePtr;
|
|
return; // if we have a match just return to the interpreter and let it execute the code
|
|
}
|
|
skipspace(p);
|
|
} while(*p == ','); // keep looping through the elements on the CASE line
|
|
checkend(p);
|
|
CurrentLinePtr = SaveCurrentLinePtr;
|
|
}
|
|
|
|
// test if we have found a CASE ELSE statement at the same level as this SELECT CASE
|
|
// if true it means that we did not find a matching CASE - so execute this code
|
|
if(tkn == cmdCASE_ELSE && i == 1) {
|
|
p+=sizeof(CommandToken); // step over the token
|
|
checkend(p);
|
|
skipelement(p);
|
|
nextstmt = p;
|
|
CurrentLinePtr = SaveCurrentLinePtr;
|
|
return;
|
|
}
|
|
|
|
if(tkn == cmdEND_SELECT) {i--; p++;} // found an END SELECT so decrement our nested counter
|
|
|
|
if(i == 0) {
|
|
// found our matching END SELECT stmt. Step over it and continue with the statement after it
|
|
skipelement(p);
|
|
nextstmt = p;
|
|
CurrentLinePtr = SaveCurrentLinePtr;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// if we have hit a CASE or CASE ELSE we must search for a END SELECT at this level and resume at that point
|
|
void cmd_case(void) {
|
|
int i;
|
|
unsigned char *p;
|
|
|
|
// search through the program looking for a END SELECT statement
|
|
// i tracks the nesting level of any nested SELECT CASE commands
|
|
i = 1; p = nextstmt;
|
|
while(1) {
|
|
p = GetNextCommand(p, NULL, (unsigned char *)"No matching END SELECT");
|
|
CommandToken tkn=commandtbl_decode(p);
|
|
if(tkn == cmdSELECT_CASE) i++; // found a nested SELECT CASE command, we now need to search for its END CASE
|
|
if(tkn == cmdEND_SELECT) i--; // found an END SELECT so decrement our nested counter
|
|
if(i == 0) {
|
|
// found our matching END SELECT stmt. Step over it and continue with the statement after it
|
|
skipelement(p);
|
|
nextstmt = p;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void cmd_input(void) {
|
|
unsigned char s[STRINGSIZE];
|
|
unsigned char *p, *sp, *tp;
|
|
int i, fnbr;
|
|
getargs(&cmdline, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)",;"); // this is a macro and must be the first executable stmt
|
|
|
|
// is the first argument a file number specifier? If so, get it
|
|
if(argc >= 3 && *argv[0] == '#') {
|
|
argv[0]++;
|
|
fnbr = getinteger(argv[0]);
|
|
i = 2;
|
|
}
|
|
else {
|
|
fnbr = 0;
|
|
// is the first argument a prompt?
|
|
// if so, print it followed by an optional question mark
|
|
if(argc >= 3 && *argv[0] == '"' && (*argv[1] == ',' || *argv[1] == ';')) {
|
|
*(argv[0] + strlen((char *)argv[0]) - 1) = 0;
|
|
argv[0]++;
|
|
MMPrintString((char *)argv[0]);
|
|
if(*argv[1] == ';') MMPrintString((char *)"? ");
|
|
i = 2;
|
|
} else {
|
|
MMPrintString((char *)"? "); // no prompt? then just print the question mark
|
|
i = 0;
|
|
}
|
|
}
|
|
|
|
if(argc - i < 1) error("Syntax"); // no variable to input to
|
|
*inpbuf = 0; // start with an empty buffer
|
|
MMgetline(fnbr, (char *)inpbuf); // get the line
|
|
p = inpbuf;
|
|
|
|
// step through the variables listed for the input statement
|
|
// and find the next item on the line and assign it to the variable
|
|
for(; i < argc; i++) {
|
|
sp = s; // sp is a temp pointer into s[]
|
|
if(*argv[i] == ',' || *argv[i] == ';') continue;
|
|
skipspace(p);
|
|
if(*p != 0) {
|
|
if(*p == '"') { // if it is a quoted string
|
|
p++; // step over the quote
|
|
while(*p && *p != '"') *sp++ = *p++; // and copy everything upto the next quote
|
|
while(*p && *p != ',') p++; // then find the next comma
|
|
} else { // otherwise it is a normal string of characters
|
|
while(*p && *p != ',') *sp++ = *p++; // copy up to the comma
|
|
while(sp > s && sp[-1] == ' ') sp--; // and trim trailing whitespace
|
|
}
|
|
}
|
|
*sp = 0; // terminate the string
|
|
tp = findvar(argv[i], V_FIND); // get the variable and save its new value
|
|
if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
|
|
if(g_vartbl[g_VarIndex].type & T_STR) {
|
|
if(strlen((char *)s) > g_vartbl[g_VarIndex].size) error("String too long");
|
|
strcpy((char *)tp, (char *)s);
|
|
CtoM(tp); // convert to a MMBasic string
|
|
} else
|
|
if(g_vartbl[g_VarIndex].type & T_INT) {
|
|
*((long long int *)tp) = strtoll((char *)s, (char **)&sp, 10); // convert to an integer
|
|
}
|
|
else
|
|
*((MMFLOAT *)tp) = (MMFLOAT)atof((char *)s);
|
|
if(*p == ',') p++;
|
|
}
|
|
}
|
|
|
|
|
|
void MIPS16 cmd_trace(void) {
|
|
if(checkstring(cmdline, (unsigned char *)"ON"))
|
|
TraceOn = true;
|
|
else if(checkstring(cmdline, (unsigned char *)"OFF"))
|
|
TraceOn = false;
|
|
else if(checkstring(cmdline, (unsigned char *)"LIST")) {
|
|
int i;
|
|
cmdline += 4;
|
|
skipspace(cmdline);
|
|
if(*cmdline == 0 || *cmdline ==(unsigned char)'\'') //'
|
|
i = TRACE_BUFF_SIZE - 1;
|
|
else
|
|
i = getint(cmdline, 0, TRACE_BUFF_SIZE - 1);
|
|
i = TraceBuffIndex - i;
|
|
if(i < 0) i += TRACE_BUFF_SIZE;
|
|
while(i != TraceBuffIndex) {
|
|
if(TraceBuff[i] >= ProgMemory && TraceBuff[i] <= ProgMemory+MAX_PROG_SIZE){
|
|
inpbuf[0] = '[';
|
|
IntToStr((char *)inpbuf + 1, CountLines(TraceBuff[i]), 10);
|
|
strcat((char *)inpbuf, "]");
|
|
}else if(TraceBuff[i]){
|
|
strcpy((char *)inpbuf, "[Lib]");
|
|
}else{
|
|
inpbuf[0] = 0;
|
|
}
|
|
MMPrintString((char *)inpbuf);
|
|
if(++i >= TRACE_BUFF_SIZE) i = 0;
|
|
}
|
|
}
|
|
else error("Unknown command");
|
|
}
|
|
|
|
|
|
|
|
// FOR command
|
|
#ifndef PICOMITE
|
|
#ifdef rp2350
|
|
void MIPS16 __not_in_flash_func(cmd_for)(void) {
|
|
#else
|
|
void cmd_for(void) {
|
|
#endif
|
|
#else
|
|
void MIPS16 __not_in_flash_func(cmd_for)(void) {
|
|
#endif
|
|
int i, t, vlen, test;
|
|
unsigned char ss[4]; // this will be used to split up the argument line
|
|
unsigned char *p, *tp, *xp;
|
|
void *vptr;
|
|
unsigned char *vname, vtype;
|
|
// static unsigned char fortoken, nexttoken;
|
|
|
|
// cache these tokens for speed
|
|
// if(!fortoken) fortoken = GetCommandValue((unsigned char *)"For");
|
|
// if(!nexttoken) nexttoken = GetCommandValue((unsigned char *)"Next");
|
|
|
|
ss[0] = tokenEQUAL;
|
|
ss[1] = tokenTO;
|
|
ss[2] = tokenSTEP;
|
|
ss[3] = 0;
|
|
{ // start a new block
|
|
getargs(&cmdline, 7, ss); // getargs macro must be the first executable stmt in a block
|
|
if(argc < 5 || argc == 6 || *argv[1] != ss[0] || *argv[3] != ss[1]) error("FOR with misplaced = or TO");
|
|
if(argc == 6 || (argc == 7 && *argv[5] != ss[2])) error("Syntax");
|
|
|
|
// get the variable name and trim any spaces
|
|
vname = argv[0];
|
|
if(*vname && *vname == ' ') vname++;
|
|
while(*vname && vname[strlen((char *)vname) - 1] == ' ') vname[strlen((char *)vname) - 1] = 0;
|
|
vlen = strlen((char *)vname);
|
|
vptr = findvar(argv[0], V_FIND); // create the variable
|
|
if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
|
|
vtype = TypeMask(g_vartbl[g_VarIndex].type);
|
|
if(vtype & T_STR) error("Invalid variable"); // sanity check
|
|
|
|
// check if the FOR variable is already in the stack and remove it if it is
|
|
// this is necessary as the program can jump out of the loop without hitting
|
|
// the NEXT statement and this will eventually result in a stack overflow
|
|
for(i = 0; i < g_forindex ;i++) {
|
|
if(g_forstack[i].var == vptr && g_forstack[i].level == g_LocalIndex) {
|
|
while(i < g_forindex - 1) {
|
|
g_forstack[i].forptr = g_forstack[i+1].forptr;
|
|
g_forstack[i].nextptr = g_forstack[i+1].nextptr;
|
|
g_forstack[i].var = g_forstack[i+1].var;
|
|
g_forstack[i].vartype = g_forstack[i+1].vartype;
|
|
g_forstack[i].level = g_forstack[i+1].level;
|
|
g_forstack[i].tovalue.i = g_forstack[i+1].tovalue.i;
|
|
g_forstack[i].stepvalue.i = g_forstack[i+1].stepvalue.i;
|
|
i++;
|
|
}
|
|
g_forindex--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(g_forindex == MAXFORLOOPS) error("Too many nested FOR loops");
|
|
|
|
g_forstack[g_forindex].var = vptr; // save the variable index
|
|
g_forstack[g_forindex].vartype = vtype; // save the type of the variable
|
|
g_forstack[g_forindex].level = g_LocalIndex; // save the level of the variable in terms of sub/funs
|
|
g_forindex++; // incase functions use for loops
|
|
if(vtype & T_NBR) {
|
|
*(MMFLOAT *)vptr = getnumber(argv[2]); // get the starting value for a float and save
|
|
g_forstack[g_forindex - 1].tovalue.f = getnumber(argv[4]); // get the to value and save
|
|
if(argc == 7)
|
|
g_forstack[g_forindex - 1].stepvalue.f = getnumber(argv[6]);// get the step value for a float and save
|
|
else
|
|
g_forstack[g_forindex - 1].stepvalue.f = 1.0; // default is +1
|
|
} else {
|
|
*(long long int *)vptr = getinteger(argv[2]); // get the starting value for an integer and save
|
|
g_forstack[g_forindex - 1].tovalue.i = getinteger(argv[4]); // get the to value and save
|
|
if(argc == 7)
|
|
g_forstack[g_forindex - 1].stepvalue.i = getinteger(argv[6]);// get the step value for an integer and save
|
|
else
|
|
g_forstack[g_forindex - 1].stepvalue.i = 1; // default is +1
|
|
}
|
|
g_forindex--;
|
|
|
|
g_forstack[g_forindex].forptr = nextstmt + 1; // return to here when looping
|
|
|
|
// now find the matching NEXT command
|
|
t = 1; p = nextstmt;
|
|
while(1) {
|
|
p = GetNextCommand(p, &tp, (unsigned char *)"No matching NEXT");
|
|
// if(*p == fortoken) t++; // count the FOR
|
|
// if(*p == nexttoken) { // is it NEXT
|
|
CommandToken tkn=commandtbl_decode(p);
|
|
|
|
if(tkn == cmdFOR) t++; // count the FOR
|
|
|
|
if(tkn == cmdNEXT) { // is it NEXT
|
|
xp = p + sizeof(CommandToken); // point to after the NEXT token
|
|
while(*xp && mystrncasecmp(xp, vname, vlen)) xp++; // step through looking for our variable
|
|
if(*xp && !isnamechar(xp[vlen])) // is it terminated correctly?
|
|
t = 0; // yes, found the matching NEXT
|
|
else
|
|
t--; // no luck, just decrement our stack counter
|
|
}
|
|
if(t == 0) { // found the matching NEXT
|
|
g_forstack[g_forindex].nextptr = p; // pointer to the start of the NEXT command
|
|
break;
|
|
}
|
|
}
|
|
|
|
// test the loop value at the start
|
|
if(g_forstack[g_forindex].vartype & T_INT)
|
|
test = (g_forstack[g_forindex].stepvalue.i >= 0 && *(long long int *)vptr > g_forstack[g_forindex].tovalue.i) || (g_forstack[g_forindex].stepvalue.i < 0 && *(long long int *)vptr < g_forstack[g_forindex].tovalue.i) ;
|
|
else
|
|
test = (g_forstack[g_forindex].stepvalue.f >= 0 && *(MMFLOAT *)vptr > g_forstack[g_forindex].tovalue.f) || (g_forstack[g_forindex].stepvalue.f < 0 && *(MMFLOAT *)vptr < g_forstack[g_forindex].tovalue.f) ;
|
|
|
|
if(test) {
|
|
// loop is invalid at the start, so go to the end of the NEXT command
|
|
skipelement(p); // find the command after the NEXT command
|
|
nextstmt = p; // this is where we will continue
|
|
} else {
|
|
g_forindex++; // save the loop data and continue on with the command after the FOR statement
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#ifndef PICOMITE
|
|
#ifdef rp2350
|
|
void MIPS16 __not_in_flash_func(cmd_next)(void) {
|
|
#else
|
|
void cmd_next(void) {
|
|
#endif
|
|
#else
|
|
void MIPS16 __not_in_flash_func(cmd_next)(void) {
|
|
#endif
|
|
int i, vindex, test;
|
|
void *vtbl[MAXFORLOOPS];
|
|
int vcnt;
|
|
unsigned char *p;
|
|
getargs(&cmdline, MAXFORLOOPS * 2, (unsigned char *)","); // getargs macro must be the first executable stmt in a block
|
|
|
|
vindex = 0; // keep lint happy
|
|
|
|
for(vcnt = i = 0; i < argc; i++) {
|
|
if(i & 0x01) {
|
|
if(*argv[i] != ',') error("Syntax");
|
|
} else
|
|
vtbl[vcnt++] = findvar(argv[i], V_FIND | V_NOFIND_ERR); // find the variable and error if not found
|
|
}
|
|
|
|
loopback:
|
|
// first search the for stack for a loop with the same variable specified on the NEXT's line
|
|
if(vcnt) {
|
|
for(i = g_forindex - 1; i >= 0; i--)
|
|
for(vindex = vcnt - 1; vindex >= 0 ; vindex--)
|
|
if(g_forstack[i].var == vtbl[vindex])
|
|
goto breakout;
|
|
} else {
|
|
// if no variables specified search the for stack looking for an entry with the same program position as
|
|
// this NEXT statement. This cheats by using the cmdline as an identifier and may not work inside an IF THEN ELSE
|
|
for(i = 0; i < g_forindex; i++) {
|
|
p = g_forstack[i].nextptr + sizeof(CommandToken);
|
|
skipspace(p);
|
|
if(p == cmdline) goto breakout;
|
|
}
|
|
}
|
|
|
|
error("Cannot find a matching FOR");
|
|
|
|
breakout:
|
|
|
|
// found a match
|
|
// apply the STEP value to the variable and test against the TO value
|
|
if(g_forstack[i].vartype & T_INT) {
|
|
*(long long int *)g_forstack[i].var += g_forstack[i].stepvalue.i;
|
|
test = (g_forstack[i].stepvalue.i >= 0 && *(long long int *)g_forstack[i].var > g_forstack[i].tovalue.i) || (g_forstack[i].stepvalue.i < 0 && *(long long int *)g_forstack[i].var < g_forstack[i].tovalue.i) ;
|
|
} else {
|
|
*(MMFLOAT *)g_forstack[i].var += g_forstack[i].stepvalue.f;
|
|
test = (g_forstack[i].stepvalue.f >= 0 && *(MMFLOAT *)g_forstack[i].var > g_forstack[i].tovalue.f) || (g_forstack[i].stepvalue.f < 0 && *(MMFLOAT *)g_forstack[i].var < g_forstack[i].tovalue.f) ;
|
|
}
|
|
|
|
if(test) {
|
|
// the loop has terminated
|
|
// remove the entry in the table, then skip forward to the next element and continue on from there
|
|
while(i < g_forindex - 1) {
|
|
g_forstack[i].forptr = g_forstack[i+1].forptr;
|
|
g_forstack[i].nextptr = g_forstack[i+1].nextptr;
|
|
g_forstack[i].var = g_forstack[i+1].var;
|
|
g_forstack[i].vartype = g_forstack[i+1].vartype;
|
|
g_forstack[i].level = g_forstack[i+1].level;
|
|
g_forstack[i].tovalue.i = g_forstack[i+1].tovalue.i;
|
|
g_forstack[i].stepvalue.i = g_forstack[i+1].stepvalue.i;
|
|
i++;
|
|
}
|
|
g_forindex--;
|
|
if(vcnt > 0) {
|
|
// remove that entry from our FOR stack
|
|
for(; vindex < vcnt - 1; vindex++) vtbl[vindex] = vtbl[vindex + 1];
|
|
vcnt--;
|
|
if(vcnt > 0)
|
|
goto loopback;
|
|
else
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
// we have not reached the terminal value yet, so go back and loop again
|
|
nextstmt = g_forstack[i].forptr;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef PICOMITE
|
|
#ifdef rp2350
|
|
void MIPS16 __not_in_flash_func(cmd_do)(void) {
|
|
#else
|
|
void cmd_do(void) {
|
|
#endif
|
|
#else
|
|
void MIPS16 __not_in_flash_func(cmd_do)(void) {
|
|
#endif
|
|
int i;
|
|
unsigned char *p, *tp, *evalp;
|
|
if(cmdtoken==cmdWHILE)error("Unknown command");
|
|
// if it is a DO loop find the WHILE token and (if found) get a pointer to its expression
|
|
while(*cmdline && *cmdline != tokenWHILE && *cmdline != tokenUNTIL) cmdline++;
|
|
if(*cmdline == tokenUNTIL)error("Syntax");
|
|
if(*cmdline == tokenWHILE) {
|
|
evalp = ++cmdline;
|
|
}
|
|
else
|
|
evalp = NULL;
|
|
// check if this loop is already in the stack and remove it if it is
|
|
// this is necessary as the program can jump out of the loop without hitting
|
|
// the LOOP or WEND stmt and this will eventually result in a stack overflow
|
|
for(i = 0; i < g_doindex ;i++) {
|
|
if(g_dostack[i].doptr == nextstmt) {
|
|
while(i < g_doindex - 1) {
|
|
g_dostack[i].evalptr = g_dostack[i+1].evalptr;
|
|
g_dostack[i].loopptr = g_dostack[i+1].loopptr;
|
|
g_dostack[i].doptr = g_dostack[i+1].doptr;
|
|
g_dostack[i].level = g_dostack[i+1].level;
|
|
i++;
|
|
}
|
|
g_doindex--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// add our pointers to the top of the stack
|
|
if(g_doindex == MAXDOLOOPS) error("Too many nested DO or WHILE loops");
|
|
g_dostack[g_doindex].evalptr = evalp;
|
|
g_dostack[g_doindex].doptr = nextstmt;
|
|
g_dostack[g_doindex].level = g_LocalIndex;
|
|
|
|
// now find the matching LOOP command
|
|
i = 1; p = nextstmt;
|
|
while(1) {
|
|
p = GetNextCommand(p, &tp, (unsigned char *)"No matching LOOP");
|
|
CommandToken tkn=commandtbl_decode(p);
|
|
if(tkn == cmdtoken) i++; // entered a nested DO or WHILE loop
|
|
if(tkn == cmdLOOP) i--; // exited a nested loop
|
|
|
|
if(i == 0) { // found our matching LOOP or WEND stmt
|
|
g_dostack[g_doindex].loopptr = p;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(g_dostack[g_doindex].evalptr != NULL) {
|
|
// if this is a DO WHILE ... LOOP statement
|
|
// search the LOOP statement for a WHILE or UNTIL token (p is pointing to the matching LOOP statement)
|
|
p+=sizeof(CommandToken);
|
|
while(*p && *p < 0x80) p++;
|
|
if(*p == tokenWHILE) error("LOOP has a WHILE test");
|
|
if(*p == tokenUNTIL) error("LOOP has an UNTIL test");
|
|
}
|
|
|
|
g_doindex++;
|
|
|
|
// do the evaluation (if there is something to evaluate) and if false go straight to the command after the LOOP or WEND statement
|
|
if(g_dostack[g_doindex - 1].evalptr != NULL && getnumber(g_dostack[g_doindex - 1].evalptr) == 0) {
|
|
g_doindex--; // remove the entry in the table
|
|
nextstmt = g_dostack[g_doindex].loopptr; // point to the LOOP or WEND statement
|
|
skipelement(nextstmt); // skip to the next command
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef PICOMITEWEB
|
|
#ifdef rp2350
|
|
void MIPS16 __not_in_flash_func(cmd_loop)(void) {
|
|
#else
|
|
void cmd_loop(void) {
|
|
#endif
|
|
#else
|
|
void MIPS16 __not_in_flash_func(cmd_loop)(void) {
|
|
#endif
|
|
unsigned char *p;
|
|
int tst = 0; // initialise tst to stop the compiler from complaining
|
|
int i;
|
|
|
|
// search the do table looking for an entry with the same program position as this LOOP statement
|
|
for(i = 0; i < g_doindex ;i++) {
|
|
p = g_dostack[i].loopptr + sizeof(CommandToken);
|
|
skipspace(p);
|
|
if(p == cmdline) {
|
|
// found a match
|
|
// first check if the DO statement had a WHILE component
|
|
// if not find the WHILE statement here and evaluate it
|
|
if(g_dostack[i].evalptr == NULL) { // if it was a DO without a WHILE
|
|
if(*cmdline >= 0x80) { // if there is something
|
|
if(*cmdline == tokenWHILE)
|
|
tst = (getnumber(++cmdline) != 0); // evaluate the expression
|
|
else if(*cmdline == tokenUNTIL)
|
|
tst = (getnumber(++cmdline) == 0); // evaluate the expression
|
|
else
|
|
error("Syntax");
|
|
}
|
|
else {
|
|
tst = 1; // and loop forever
|
|
checkend(cmdline); // make sure that there is nothing else
|
|
}
|
|
}
|
|
else { // if was DO WHILE
|
|
tst = (getnumber(g_dostack[i].evalptr) != 0); // evaluate its expression
|
|
checkend(cmdline); // make sure that there is nothing else
|
|
}
|
|
|
|
// test the expression value and reset the program pointer if we are still looping
|
|
// otherwise remove this entry from the do stack
|
|
if(tst)
|
|
nextstmt = g_dostack[i].doptr; // loop again
|
|
else {
|
|
// the loop has terminated
|
|
// remove the entry in the table, then just let the default nextstmt run and continue on from there
|
|
g_doindex = i;
|
|
// just let the default nextstmt run
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
error("LOOP without a matching DO");
|
|
}
|
|
|
|
|
|
|
|
void cmd_exitfor(void) {
|
|
if(g_forindex == 0) error("No FOR loop is in effect");
|
|
nextstmt = g_forstack[--g_forindex].nextptr;
|
|
checkend(cmdline);
|
|
skipelement(nextstmt);
|
|
}
|
|
|
|
|
|
|
|
void cmd_exit(void) {
|
|
if(g_doindex == 0) error("No DO loop is in effect");
|
|
nextstmt = g_dostack[--g_doindex].loopptr;
|
|
checkend(cmdline);
|
|
skipelement(nextstmt);
|
|
}
|
|
|
|
|
|
|
|
/*void cmd_error(void) {
|
|
unsigned char *s;
|
|
if(*cmdline && *cmdline != '\'') {
|
|
s = getCstring(cmdline);
|
|
char *p=GetTempMemory(STRINGSIZE);
|
|
strcpy(p,"[");
|
|
int ln=CountLines(CurrentLinePtr);
|
|
IntToStr(&p[1],ln,10);
|
|
SaveCurrentLinePtr=CurrentLinePtr;
|
|
CurrentLinePtr = NULL; // suppress printing the line that caused the issue
|
|
strcat((char *)p,"] ");
|
|
strcat((char *)p,(char *)s);
|
|
error(p);
|
|
}
|
|
else
|
|
error("");
|
|
}*/
|
|
void cmd_error(void) {
|
|
unsigned char *s;
|
|
if(*cmdline && *cmdline != '\'') {
|
|
s = getCstring(cmdline);
|
|
// CurrentLinePtr = NULL; // suppress printing the line that caused the issue
|
|
error((char *) s);
|
|
}
|
|
else
|
|
error("");
|
|
}
|
|
|
|
|
|
#ifndef rp2350
|
|
void cmd_randomize(void) {
|
|
int i;
|
|
getargs(&cmdline,1,(unsigned char *)",");
|
|
if(argc==1)i = getinteger(argv[0]);
|
|
else i=time_us_32();
|
|
if(i < 0) error("Number out of bounds");
|
|
srand(i);
|
|
}
|
|
#endif
|
|
|
|
// this is the Sub or Fun command
|
|
// it simply skips over text until it finds the end of it
|
|
void cmd_subfun(void) {
|
|
unsigned char *p;
|
|
unsigned short returntoken, errtoken;
|
|
|
|
if(gosubindex != 0) error("No matching END declaration"); // we have hit a SUB/FUN while in another SUB or FUN
|
|
if(cmdtoken == cmdSUB) {
|
|
returntoken = cmdENDSUB;
|
|
errtoken = cmdENDFUNCTION;
|
|
} else {
|
|
returntoken = cmdENDFUNCTION;
|
|
errtoken = cmdENDSUB;
|
|
}
|
|
p = nextstmt;
|
|
while(1) {
|
|
p = GetNextCommand(p, NULL, (unsigned char *)"No matching END declaration");
|
|
CommandToken tkn=commandtbl_decode(p);
|
|
if(tkn == cmdSUB || tkn == cmdFUN || tkn == errtoken) error("No matching END declaration");
|
|
if(tkn == returntoken) { // found the next return
|
|
skipelement(p);
|
|
nextstmt = p; // point to the next command
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// this is the Sub or Fun command
|
|
// it simply skips over text until it finds the end of it
|
|
void cmd_comment(void) {
|
|
unsigned char *p;
|
|
unsigned short returntoken;
|
|
|
|
returntoken = GetCommandValue((unsigned char *)"*/");
|
|
// errtoken = cmdENDSUB;
|
|
p = nextstmt;
|
|
while(1) {
|
|
p = GetNextCommand(p, NULL, (unsigned char *)"No matching END declaration");
|
|
CommandToken tkn=commandtbl_decode(p);
|
|
if(tkn == cmdComment) error("No matching END declaration");
|
|
if(tkn == returntoken) { // found the next return
|
|
skipelement(p);
|
|
nextstmt = p; // point to the next command
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
void cmd_endcomment(void){
|
|
|
|
}
|
|
|
|
|
|
void cmd_gosub(void) {
|
|
if(gosubindex >= MAXGOSUB) error("Too many nested GOSUB");
|
|
char *return_to = (char *)nextstmt;
|
|
if(isnamestart(*cmdline))
|
|
nextstmt = findlabel(cmdline);
|
|
else
|
|
nextstmt = findline(getinteger(cmdline), true);
|
|
IgnorePIN = false;
|
|
|
|
errorstack[gosubindex] = CurrentLinePtr;
|
|
gosubstack[gosubindex++] = (unsigned char *)return_to;
|
|
g_LocalIndex++;
|
|
CurrentLinePtr = nextstmt;
|
|
}
|
|
|
|
void cmd_mid(void){
|
|
unsigned char *p;
|
|
getargs(&cmdline,5,(unsigned char *)",");
|
|
findvar(argv[0], V_NOFIND_ERR);
|
|
if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
|
|
if(!(g_vartbl[g_VarIndex].type & T_STR)) error("Not a string");
|
|
int size=g_vartbl[g_VarIndex].size;
|
|
char *sourcestring=(char *)getstring(argv[0]);
|
|
int start=getint(argv[2],1,sourcestring[0]);
|
|
int num=0;
|
|
if(argc==5)num=getint(argv[4],1,sourcestring[0]);
|
|
if(start+num-1>sourcestring[0])error("Selection exceeds length of string");
|
|
while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++;
|
|
if(!*cmdline) error("Syntax");
|
|
++cmdline;
|
|
if(!*cmdline) error("Syntax");
|
|
char *value = (char *)getstring(cmdline);
|
|
if(num==0)num=value[0];
|
|
p=(unsigned char *)&value[1];
|
|
if(num==value[0]) memcpy(&sourcestring[start],p,num);
|
|
else {
|
|
int change=value[0]-num;
|
|
if(sourcestring[0]+change>size)error("String too long");
|
|
memmove(&sourcestring[start+value[0]],&sourcestring[start+num],sourcestring[0]-(start+num-1));
|
|
sourcestring[0]+=change;
|
|
memcpy(&sourcestring[start],p,value[0]);
|
|
}
|
|
}
|
|
void cmd_byte(void){
|
|
getargs(&cmdline,3,(unsigned char *)",");
|
|
findvar(argv[0], V_NOFIND_ERR);
|
|
if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
|
|
if(!(g_vartbl[g_VarIndex].type & T_STR)) error("Not a string");
|
|
unsigned char *sourcestring=(unsigned char *)getstring(argv[0]);
|
|
int start=getint(argv[2],1,sourcestring[0]);
|
|
while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++;
|
|
if(!*cmdline) error("Syntax");
|
|
++cmdline;
|
|
if(!*cmdline) error("Syntax");
|
|
int value = getint(cmdline,0,255);
|
|
sourcestring[start]=value;
|
|
}
|
|
void cmd_bit(void){
|
|
getargs(&cmdline,3,(unsigned char *)",");
|
|
uint64_t *source=(uint64_t *)findvar(argv[0], V_NOFIND_ERR);
|
|
if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
|
|
if(!(g_vartbl[g_VarIndex].type & T_INT)) error("Not an integer");
|
|
uint64_t bit=(uint64_t)1<<(uint64_t)getint(argv[2],0,63);
|
|
while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++;
|
|
if(!*cmdline) error("Syntax");
|
|
++cmdline;
|
|
if(!*cmdline) error("Syntax");
|
|
int value = getint(cmdline,0,1);
|
|
if(value)*source|=bit;
|
|
else *source&=(~bit);
|
|
}
|
|
void cmd_flags(void) {
|
|
while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++;
|
|
if(!*cmdline) error("Syntax");
|
|
g_flag=getinteger(++cmdline);
|
|
}
|
|
|
|
void cmd_flag(void){
|
|
getargs(&cmdline,1,(unsigned char *)",");
|
|
uint64_t bit=(uint64_t)1<<(uint64_t)getint(argv[0],0,63);
|
|
while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++;
|
|
if(!*cmdline) error("Syntax");
|
|
++cmdline;
|
|
if(!*cmdline) error("Syntax");
|
|
int value = getint(cmdline,0,1);
|
|
if(value)g_flag |=bit;
|
|
else g_flag &=~bit;
|
|
}
|
|
|
|
void MIPS16 __not_in_flash_func(cmd_return)(void) {
|
|
checkend(cmdline);
|
|
if(gosubindex == 0 || gosubstack[gosubindex - 1] == NULL) error("Nothing to return to");
|
|
ClearVars(g_LocalIndex--, true); // delete any local variables
|
|
g_TempMemoryIsChanged = true; // signal that temporary memory should be checked
|
|
nextstmt = gosubstack[--gosubindex]; // return to the caller
|
|
CurrentLinePtr = errorstack[gosubindex];
|
|
}
|
|
/*frame
|
|
#define c_topleft 218
|
|
#define c_topright 191
|
|
#define c_bottomleft 192
|
|
#define c_bottomright 217
|
|
#define c_horizontal 196
|
|
#define c_vertical 179
|
|
#define c_cross 197
|
|
#define c_tup 193
|
|
#define c_tdown 194
|
|
#define c_tleft 195
|
|
#define c_tright 180
|
|
#define c_d_topleft 201
|
|
#define c_d_topright 187
|
|
#define c_d_bottomleft 200
|
|
#define c_d_bottomright 188
|
|
#define c_d_horizontal 205
|
|
#define c_d_vertical 186
|
|
#define c_d_cross 206
|
|
#define c_d_tup 202
|
|
#define c_d_tdown 203
|
|
#define c_d_tleft 204
|
|
#define c_d_tright 185
|
|
|
|
|
|
extern const int colours[16];
|
|
extern void setterminal(void);
|
|
unsigned short *frame=NULL, *outframe=NULL;
|
|
bool framecursor=true;
|
|
static int framex=0,framey=0;
|
|
static inline void framewritechar(int x, int y, uint8_t ascii, uint8_t fcolour, uint8_t attributes){
|
|
if(x>=framex || y>=framey)return;
|
|
frame[(y*framex)+x]=ascii | (fcolour<<8) | (attributes<<12);
|
|
}
|
|
static inline uint16_t framegetchar(int x, int y ){
|
|
return frame[(y*framex)+x];
|
|
}
|
|
static void SCursor(int x, int y) {
|
|
char s[30];
|
|
ShowCursor(0);
|
|
CurrentX=x*gui_font_width;
|
|
CurrentY=y*gui_font_height;
|
|
sprintf(s,"\033[%d;%dH",y+1,x+1);
|
|
SSPrintString(s);
|
|
ShowCursor(framecursor);
|
|
}
|
|
static void SColour(int colour, int fore){
|
|
char s[24]={0};
|
|
int r=colour>>16;
|
|
int g=(colour>>8) & 0xFF;
|
|
int b=colour & 0xff;
|
|
if(fore){
|
|
strcpy(s,"\033[38;2;");
|
|
gui_fcolour=colour;
|
|
} else {
|
|
strcpy(s,"\033[48;2;");
|
|
gui_bcolour=colour;
|
|
}
|
|
sprintf(&s[7],"%d;%d;%dm",r,g,b);
|
|
SSPrintString(s);
|
|
}
|
|
|
|
void cmd_frame(void){
|
|
unsigned char *p=NULL;
|
|
if((p=checkstring(cmdline,(unsigned char *)"CREATE"))){
|
|
if(frame)error("Frame already exists");
|
|
framex=HRes/gui_font_width;
|
|
framey=VRes/gui_font_height;
|
|
frame=(uint16_t *)GetMemory(framex*framey*sizeof(uint16_t));
|
|
outframe=(uint16_t *)GetMemory(framex*framey*sizeof(uint16_t));
|
|
for(int i=0;i<framex*framey;i++)outframe[i]=0xFFFF;
|
|
Option.Width=framex;
|
|
Option.Height=framey;
|
|
char sp[20]={0};
|
|
strcpy(sp,"\033[8;");
|
|
IntToStr(&sp[strlen(sp)],framey,10);
|
|
strcat(sp,";");
|
|
IntToStr(&sp[strlen(sp)],framex+1,10);
|
|
strcat(sp,"t");
|
|
SSPrintString(sp);
|
|
SSPrintString("\0337\033[2J\033[H"); // vt100 clear screen and home cursor
|
|
ClearScreen(gui_bcolour); //
|
|
return;
|
|
}
|
|
if(!frame)error("Frame not created");
|
|
if((p=checkstring(cmdline,(unsigned char *)"CURSOR"))){
|
|
if(checkstring(p,(unsigned char *)"ON")){
|
|
framecursor=true;
|
|
SSPrintString("\033[?25h");
|
|
} else if(checkstring(p,(unsigned char *)"OFF")){
|
|
ShowCursor(0);
|
|
framecursor=false;
|
|
SSPrintString("\033[?25l");
|
|
} else {
|
|
getargs(&p,3,(unsigned char *)",");
|
|
if(argc<3)error("Syntax");
|
|
int x=getint(argv[0],0,framex-1);
|
|
int y=getint(argv[2],0,framey-1);
|
|
if(DISPLAY_TYPE==SCREENMODE1)tilefcols[y*X_TILE+x]=RGB121pack(gui_fcolour);
|
|
SCursor(x,y);
|
|
}
|
|
} else if((p=checkstring(cmdline,(unsigned char *)"BOX"))){
|
|
int fc=gui_fcolour;
|
|
bool dual=false;
|
|
getargs(&p,11,(unsigned char *)",");
|
|
if(argc<7)error("Syntax");
|
|
int x=getint(argv[0],0,framex-1);
|
|
int y=getint(argv[2],0,framey-1);
|
|
int w=getint(argv[4],1,framex-1-x);
|
|
int h=getint(argv[6],1,framey-1-y);
|
|
if(argc>=9 && *argv[8])fc=getint(argv[8],0,WHITE);
|
|
if(argc==11){
|
|
if(checkstring(argv[10],(unsigned char *)"DOUBLE"))dual=true;
|
|
}
|
|
fc=RGB121(fc);
|
|
framewritechar(x,y,dual ? c_d_topleft : c_topleft,fc,0);
|
|
framewritechar(x+w,y,dual ? c_d_topright : c_topright,fc,0);
|
|
framewritechar(x+w,y+h,dual ? c_d_bottomright : c_bottomright,fc,0);
|
|
framewritechar(x,y+h,dual ? c_d_bottomleft : c_bottomleft,fc,0);
|
|
for(int i=x+1;i<x+w;i++){
|
|
framewritechar(i,y,dual ? c_d_horizontal : c_horizontal,fc,0);
|
|
framewritechar(i,y+h,dual ? c_d_horizontal : c_horizontal,fc,0);
|
|
}
|
|
for(int i=y+1;i<y+h;i++){
|
|
framewritechar(x,i,dual ? c_d_vertical : c_vertical,fc,0);
|
|
framewritechar(x+w,i,dual ? c_d_vertical : c_vertical,fc,0);
|
|
}
|
|
SCursor(1,1);
|
|
} else if((p=checkstring(cmdline,(unsigned char *)"QBOX"))){
|
|
int fc=gui_fcolour;
|
|
bool dual=false;
|
|
getargs(&p,15,(unsigned char *)",");
|
|
if(argc<9)error("Syntax");
|
|
int x=getint(argv[0],0,framex-1);
|
|
int y=getint(argv[2],0,framey-1);
|
|
int w=getint(argv[4],1,framex-1-x);
|
|
int w2=getint(argv[6],1,framex-1-x-w);
|
|
int h=getint(argv[8],1,framey-1-y);
|
|
int h2=getint(argv[10],1,framey-1-y-h);
|
|
if(argc>=13 && *argv[12])fc=getint(argv[12],0,WHITE);
|
|
if(argc==15){
|
|
if(checkstring(argv[14],(unsigned char *)"DOUBLE"))dual=true;
|
|
}
|
|
fc=RGB121(fc);
|
|
framewritechar(x,y,dual ? c_d_topleft : c_topleft,fc,0);
|
|
framewritechar(x+w,y,dual ? c_d_tdown : c_tdown,fc,0);
|
|
framewritechar(x+w+w2,y,dual ? c_d_topright : c_topright,fc,0);
|
|
framewritechar(x,y+h,dual ? c_d_tleft : c_tleft,fc,0);
|
|
framewritechar(x+w,y+h,dual ? c_d_cross : c_cross,fc,0);
|
|
framewritechar(x+w+w2,y+h,dual ? c_d_tright : c_tright,fc,0);
|
|
framewritechar(x,y+h+h2,dual ? c_d_bottomleft : c_bottomleft,fc,0);
|
|
framewritechar(x+w,y+h+h2,dual ? c_d_tup : c_tup,fc,0);
|
|
framewritechar(x+w+w2,y+h+h2,dual ? c_d_bottomright : c_bottomright,fc,0);
|
|
for(int i=x+1;i<x+w;i++){
|
|
framewritechar(i,y,dual ? c_d_horizontal : c_horizontal,fc,0);
|
|
framewritechar(i,y+h,dual ? c_d_horizontal : c_horizontal,fc,0);
|
|
framewritechar(i,y+h+h2,dual ? c_d_horizontal : c_horizontal,fc,0);
|
|
}
|
|
for(int i=x+1+w;i<x+w+w2;i++){
|
|
framewritechar(i,y,dual ? c_d_horizontal : c_horizontal,fc,0);
|
|
framewritechar(i,y+h,dual ? c_d_horizontal : c_horizontal,fc,0);
|
|
framewritechar(i,y+h+h2,dual ? c_d_horizontal : c_horizontal,fc,0);
|
|
}
|
|
for(int i=y+1;i<y+h;i++){
|
|
framewritechar(x,i,dual ? c_d_vertical : c_vertical,fc,0);
|
|
framewritechar(x+w,i,dual ? c_d_vertical : c_vertical,fc,0);
|
|
framewritechar(x+w+w2,i,dual ? c_d_vertical : c_vertical,fc,0);
|
|
}
|
|
for(int i=y+1+h;i<y+h+h2;i++){
|
|
framewritechar(x,i,dual ? c_d_vertical : c_vertical,fc,0);
|
|
framewritechar(x+w,i,dual ? c_d_vertical : c_vertical,fc,0);
|
|
framewritechar(x+w+w2,i,dual ? c_d_vertical : c_vertical,fc,0);
|
|
}
|
|
SCursor(1,1);
|
|
} else if((p=checkstring(cmdline,(unsigned char *)"VBOX"))){
|
|
int fc=gui_fcolour;
|
|
bool dual=false;
|
|
getargs(&p,13,(unsigned char *)",");
|
|
if(argc<9)error("Syntax");
|
|
int x=getint(argv[0],0,framex-1);
|
|
int y=getint(argv[2],0,framey-1);
|
|
int w=getint(argv[4],1,framex-1-x);
|
|
int h=getint(argv[6],1,framey-1-y);
|
|
int h2=getint(argv[8],1,framey-1-y-h);
|
|
if(argc>=11 && *argv[10])fc=getint(argv[10],0,WHITE);
|
|
if(argc==13){
|
|
if(checkstring(argv[12],(unsigned char *)"DOUBLE"))dual=true;
|
|
}
|
|
fc=RGB121(fc);
|
|
framewritechar(x,y,dual ? c_d_topleft : c_d_topleft,fc,0);
|
|
framewritechar(x+w,y,dual ? c_d_topright : c_d_topright,fc,0);
|
|
framewritechar(x+w,y+h,dual ? c_d_tright : c_d_tright,fc,0);
|
|
framewritechar(x+w,y+h+h2,dual ? c_d_bottomright : c_d_bottomright,fc,0);
|
|
framewritechar(x,y+h+h2,dual ? c_d_bottomleft : c_d_bottomleft,fc,0);
|
|
framewritechar(x,y+h,dual ? c_d_tleft : c_d_tleft,fc,0);
|
|
for(int i=x+1;i<x+w;i++){
|
|
framewritechar(i,y,dual ? c_d_horizontal : c_d_horizontal,fc,0);
|
|
framewritechar(i,y+h,dual ? c_d_horizontal : c_d_horizontal,fc,0);
|
|
framewritechar(i,y+h+h2,dual ? c_d_horizontal : c_d_horizontal,fc,0);
|
|
}
|
|
for(int i=y+1;i<y+h;i++){
|
|
framewritechar(x,i,dual ? c_d_vertical : c_d_vertical,fc,0);
|
|
framewritechar(x+w,i,dual ? c_d_vertical : c_d_vertical,fc,0);
|
|
}
|
|
for(int i=y+1+h;i<y+h+h2;i++){
|
|
framewritechar(x,i,dual ? c_d_vertical : c_d_vertical,fc,0);
|
|
framewritechar(x+w,i,dual ? c_d_vertical : c_d_vertical,fc,0);
|
|
}
|
|
SCursor(1,1);
|
|
} else if((p=checkstring(cmdline,(unsigned char *)"HBOX"))){
|
|
int fc=gui_fcolour;
|
|
bool dual=false;
|
|
getargs(&p,13,(unsigned char *)",");
|
|
if(argc<9)error("Syntax");
|
|
int x=getint(argv[0],0,framex-1);
|
|
int y=getint(argv[2],0,framey-1);
|
|
int w=getint(argv[4],1,framex-1-x);
|
|
int w2=getint(argv[6],1,framex-1-x-w);
|
|
int h=getint(argv[8],1,framey-1-y);
|
|
if(argc>=11 && *argv[10])fc=getint(argv[10],0,WHITE);
|
|
if(argc==13){
|
|
if(checkstring(argv[12],(unsigned char *)"DOUBLE"))dual=true;
|
|
}
|
|
fc=RGB121(fc);
|
|
framewritechar(x,y,dual ? c_d_topleft : c_topleft,fc,0);
|
|
framewritechar(x+w,y,dual ? c_d_tdown : c_tdown,fc,0);
|
|
framewritechar(x+w,y+h,dual ? c_d_tup : c_tup,fc,0);
|
|
framewritechar(x+w+w2,y,dual ? c_d_topright : c_topright,fc,0);
|
|
framewritechar(x+w+w2,y+h,dual ? c_d_bottomright : c_bottomright,fc,0);
|
|
framewritechar(x,y+h,dual ? c_d_bottomleft : c_bottomleft,fc,0);
|
|
for(int i=x+1;i<x+w;i++){
|
|
framewritechar(i,y,dual ? c_d_horizontal : c_horizontal,fc,0);
|
|
framewritechar(i,y+h,dual ? c_d_horizontal : c_horizontal,fc,0);
|
|
}
|
|
for(int i=x+1+w;i<x+w+w2;i++){
|
|
framewritechar(i,y,dual ? c_d_horizontal : c_horizontal,fc,0);
|
|
framewritechar(i,y+h,dual ? c_d_horizontal : c_horizontal,fc,0);
|
|
}
|
|
for(int i=y+1;i<y+h;i++){
|
|
framewritechar(x,i,dual ? c_d_vertical : c_vertical,fc,0);
|
|
framewritechar(x+w,i,dual ? c_d_vertical : c_vertical,fc,0);
|
|
framewritechar(x+w+w2,i,dual ? c_d_vertical : c_vertical,fc,0);
|
|
}
|
|
SCursor(1,1);
|
|
} else if((p=checkstring(cmdline,(unsigned char *)"CLEAR"))){
|
|
memset(frame,0,framex*framey*sizeof(uint16_t));
|
|
} else if((p=checkstring(cmdline,(unsigned char *)"CLOSE"))){
|
|
if(!frame)error("Frame does not exist");
|
|
FreeMemorySafe((void **)&frame);
|
|
FreeMemorySafe((void **)&outframe);
|
|
} else if((p=checkstring(cmdline,(unsigned char *)"SCROLL"))){
|
|
int xstart=0,ystart=0,xend=framex-1,yend=framey-1, xmax=framex-1, ymax=framey-1;
|
|
getargs(&p,11,(unsigned char *)",");
|
|
// if(argc<7)error("Syntax");
|
|
int xs=getint(argv[0],-(xmax),xmax);
|
|
int ys=getint(argv[2],-(ymax),ymax);
|
|
if(argc>=5 && *argv[4])xstart=getint(argv[4],0,xmax);
|
|
if(argc>=7 && *argv[6])ystart=getint(argv[6],0,ymax);
|
|
if(argc>=9 && *argv[8])xend=getint(argv[8],1,xmax);
|
|
if(argc==11)yend=getint(argv[10],1,ymax);
|
|
SCursor(xstart,ystart);
|
|
if(DISPLAY_TYPE==SCREENMODE1)tilefcols[ystart*X_TILE+xstart]=RGB;
|
|
if(abs(xs)>=0){
|
|
for(int y=ystart;y<=yend;y++){
|
|
uint16_t *line=&frame[y*framex];
|
|
if(xs>0){
|
|
memmove((uint8_t*)&line[xstart+xs],(uint8_t*)&line[xstart],(xend-xstart-xs+1)*sizeof(uint16_t));
|
|
memset((uint8_t*)&line[xstart],0,xs*sizeof(uint16_t));
|
|
} else {
|
|
memmove((uint8_t*)&line[xstart],(uint8_t*)&line[xstart-xs],(xend-xstart+xs+1)*sizeof(uint16_t));
|
|
memset((uint8_t*)&line[xend+xs+1],0,abs(xs)*sizeof(uint16_t));
|
|
}
|
|
}
|
|
}
|
|
if(ys>0){
|
|
uint16_t *line1=&frame[(yend)*framex+xstart];
|
|
uint16_t *line2=&frame[(yend-ys)*framex+xstart];
|
|
for(int y=yend;y>ystart-ys+1;y--){
|
|
memcpy((uint8_t*)line1,(uint8_t*)line2,(xend-xstart+1)*sizeof(uint16_t));
|
|
line1-=framex;
|
|
line2-=framex;
|
|
}
|
|
line1=&frame[ystart*framex+xstart];
|
|
for(int y=ystart;y<ystart+ys;y++){
|
|
memset((uint8_t*)line1,0,(xend-xstart+1)*sizeof(uint16_t));
|
|
line1+=framex;
|
|
}
|
|
} else if(ys<0){
|
|
ys=-ys;
|
|
uint16_t *line1=&frame[ystart*framex+xstart];
|
|
uint16_t *line2=&frame[(ystart+ys)*framex+xstart];
|
|
for(int y=ystart;y<=yend-ys;y++){
|
|
memcpy((uint8_t*)line1,(uint8_t*)line2,(xend-xstart+1)*sizeof(uint16_t));
|
|
line1+=framex;
|
|
line2+=framex;
|
|
}
|
|
line1=&frame[(yend-ys+1)*framex+xstart];
|
|
for(int y=yend-ys+1;y<=yend;y++){
|
|
memset((uint8_t*)line1,0,(xend-xstart+1)*sizeof(uint16_t));
|
|
line1+=framex;
|
|
}
|
|
}
|
|
} else if((p=checkstring(cmdline,(unsigned char *)"WRITE"))){
|
|
int savefcol=gui_fcolour;
|
|
int lasty=-1,lastx=-1,lastc=-1;
|
|
int sx=CurrentX/gui_font_width,sy=CurrentY/gui_font_height;
|
|
int ccursor=framecursor;
|
|
framecursor=0;
|
|
ShowCursor(0);
|
|
SColour(gui_bcolour,0);
|
|
for(int y=0;y<framey;y++){
|
|
for(int x=0;x<framex;x++){
|
|
uint16_t c=framegetchar(x,y);
|
|
if(c!=outframe[(y*framex)+x]){
|
|
outframe[(y*framex)+x]=c;
|
|
if(y!=lasty || x!=lastx){
|
|
SCursor(x,y);
|
|
lastx=x+1;
|
|
lasty=y;
|
|
} else lastx=x+1;
|
|
int outc=colours[(c>>8) & 0xF];
|
|
if(outc!=lastc){
|
|
lastc=outc;
|
|
SColour(outc,1);
|
|
}
|
|
if(c==0)c=' ';
|
|
DisplayPutC(c&0xFF);
|
|
SerialConsolePutC(c&0xFF,0);
|
|
}
|
|
}
|
|
}
|
|
fflush(stdout);
|
|
gui_fcolour=savefcol;
|
|
SCursor(sx,sy);
|
|
framecursor=ccursor;
|
|
ShowCursor(framecursor);
|
|
if(DISPLAY_TYPE==SCREENMODE1)tilefcols[sy*X_TILE+sx]=Option.VGAFC;
|
|
SColour(gui_fcolour,1);
|
|
} else {
|
|
int attributes=0, fc=gui_fcolour;
|
|
getargs(&cmdline,9,(unsigned char *)",");
|
|
if(argc<5)error("Syntax");
|
|
int x=getint(argv[0],0,framex-1);
|
|
int y=getint(argv[2],0,framey-1);
|
|
p=getCstring(argv[4]);
|
|
if(argc>=7 && *argv[6])fc=getint(argv[6],0,WHITE);
|
|
if(argc==9)attributes=getint(argv[8],0,15);
|
|
int l=strlen((char *)p);
|
|
fc=RGB121(fc);
|
|
while(l--){
|
|
if(x==framex){y++;x=0;}
|
|
if(y==framey)return;
|
|
framewritechar(x++,y,*p++,fc,attributes);
|
|
}
|
|
}
|
|
}*/
|
|
|
|
|
|
void cmd_endfun(void) {
|
|
checkend(cmdline);
|
|
if(gosubindex == 0 || gosubstack[gosubindex - 1] != NULL) error("Nothing to return to");
|
|
nextstmt = (unsigned char *)"\0\0\0"; // now terminate this run of ExecuteProgram()
|
|
}
|
|
|
|
|
|
|
|
void MIPS16 cmd_read(void) {
|
|
int i, j, k, len, card;
|
|
unsigned char *p, *lineptr = NULL, *ptr;
|
|
unsigned short datatoken;
|
|
int vcnt, vidx, num_to_read=0;
|
|
if (checkstring(cmdline, (unsigned char*)"SAVE")) {
|
|
if(restorepointer== MAXRESTORE - 1)error((char*)"Too many saves");
|
|
datastore[restorepointer].SaveNextDataLine = NextDataLine;
|
|
datastore[restorepointer].SaveNextData = NextData;
|
|
restorepointer++;
|
|
return;
|
|
}
|
|
if (checkstring(cmdline, (unsigned char*)"RESTORE")) {
|
|
if (!restorepointer)error((char*)"Nothing to restore");
|
|
restorepointer--;
|
|
NextDataLine = datastore[restorepointer].SaveNextDataLine;
|
|
NextData = datastore[restorepointer].SaveNextData;
|
|
return;
|
|
}
|
|
getargs(&cmdline, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); // getargs macro must be the first executable stmt in a block
|
|
if(argc == 0) error("Syntax");
|
|
// first count the elements and do the syntax checking
|
|
for(vcnt = i = 0; i < argc; i++) {
|
|
if(i & 0x01) {
|
|
if(*argv[i] != ',') error("Syntax");
|
|
} else {
|
|
findvar(argv[i], V_FIND | V_EMPTY_OK);
|
|
if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
|
|
card=1;
|
|
if(emptyarray){ //empty array
|
|
for(k=0;k<MAXDIM;k++){
|
|
j=(g_vartbl[g_VarIndex].dims[k] - g_OptionBase + 1);
|
|
if(j)card *= j;
|
|
}
|
|
}
|
|
num_to_read+=card;
|
|
}
|
|
}
|
|
char **vtbl=GetTempMemory(num_to_read * sizeof (char *));
|
|
int *vtype=GetTempMemory(num_to_read * sizeof (int));
|
|
int *vsize=GetTempMemory(num_to_read * sizeof (int));
|
|
// step through the arguments and save the pointer and type
|
|
for(vcnt = i = 0; i < argc; i+=2) {
|
|
ptr = findvar(argv[i], V_FIND | V_EMPTY_OK);
|
|
vtbl[vcnt] = (char *)ptr;
|
|
card=1;
|
|
if(emptyarray){ //empty array
|
|
for(k=0;k<MAXDIM;k++){
|
|
j=(g_vartbl[g_VarIndex].dims[k] - g_OptionBase + 1);
|
|
if(j)card *= j;
|
|
}
|
|
}
|
|
for(k=0;k<card;k++){
|
|
if(k){
|
|
if(g_vartbl[g_VarIndex].type & (T_INT | T_NBR))ptr+=8;
|
|
else ptr+=g_vartbl[g_VarIndex].size+1;
|
|
vtbl[vcnt]=(char *)ptr;
|
|
}
|
|
vtype[vcnt] = TypeMask(g_vartbl[g_VarIndex].type);
|
|
vsize[vcnt] = g_vartbl[g_VarIndex].size;
|
|
vcnt++;
|
|
}
|
|
}
|
|
|
|
// setup for a search through the whole memory
|
|
vidx = 0;
|
|
datatoken = GetCommandValue((unsigned char *)"Data");
|
|
p = lineptr = NextDataLine;
|
|
if(*p == 0xff) error("No DATA to read"); // error if there is no program
|
|
|
|
// search looking for a DATA statement. We keep returning to this point until all the data is found
|
|
search_again:
|
|
while(1) {
|
|
if(*p == 0) p++; // if it is at the end of an element skip the zero marker
|
|
if(*p == 0/* || *p == 0xff*/) error("No DATA to read"); // end of the program and we still need more data
|
|
if(*p == T_NEWLINE) lineptr = p++;
|
|
if(*p == T_LINENBR) p += 3;
|
|
skipspace(p);
|
|
if(*p == T_LABEL) { // if there is a label here
|
|
p += p[1] + 2; // skip over the label
|
|
skipspace(p); // and any following spaces
|
|
}
|
|
CommandToken tkn=commandtbl_decode(p);
|
|
if(tkn == datatoken) break; // found a DATA statement
|
|
while(*p) p++; // look for the zero marking the start of the next element
|
|
}
|
|
NextDataLine = lineptr;
|
|
p+=sizeof(CommandToken); // step over the token
|
|
skipspace(p);
|
|
if(!*p || *p == '\'') { CurrentLinePtr = lineptr; error("No DATA to read"); }
|
|
|
|
// we have a DATA statement, first split the line into arguments
|
|
{ // new block, the getargs macro must be the first executable stmt in a block
|
|
getargs(&p, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)",");
|
|
if((argc & 1) == 0) { CurrentLinePtr = lineptr; error("Syntax"); }
|
|
// now step through the variables on the READ line and get their new values from the argument list
|
|
// we set the line number to the number of the DATA stmt so that any errors are reported correctly
|
|
while(vidx < vcnt) {
|
|
// check that there is some data to read if not look for another DATA stmt
|
|
if(NextData > argc) {
|
|
skipline(p);
|
|
NextData = 0;
|
|
goto search_again;
|
|
}
|
|
CurrentLinePtr = lineptr;
|
|
if(vtype[vidx] & T_STR) {
|
|
char *p1, *p2;
|
|
if(*argv[NextData] == '"') { // if quoted string
|
|
int toggle=0;
|
|
for(len = 0, p1 = vtbl[vidx], p2 = (char *)argv[NextData] + 1; *p2 && *p2 != '"'; len++) {
|
|
if(*p2=='\\' && p2[1]!='"' && OptionEscape)toggle^=1;
|
|
if(toggle){
|
|
if(*p2=='\\' && isdigit((unsigned char)p2[1]) && isdigit((unsigned char)p2[2]) && isdigit((unsigned char)p2[3])){
|
|
p2++;
|
|
i=(*p2++)-48;
|
|
i*=10;
|
|
i+=(*p2++)-48;
|
|
i*=10;
|
|
i+=(*p2++)-48;
|
|
if(i==0)error("Null character \\000 in escape sequence - use CHR$(0)","$");
|
|
*p1++=i;
|
|
} else {
|
|
p2++;
|
|
switch(*p2){
|
|
case '\\':
|
|
*p1++='\\';
|
|
p2++;
|
|
break;
|
|
case 'a':
|
|
*p1++='\a';
|
|
p2++;
|
|
break;
|
|
case 'b':
|
|
*p1++='\b';
|
|
p2++;
|
|
break;
|
|
case 'e':
|
|
*p1++='\e';
|
|
p2++;
|
|
break;
|
|
case 'f':
|
|
*p1++='\f';
|
|
p2++;
|
|
break;
|
|
case 'n':
|
|
*p1++='\n';
|
|
p2++;
|
|
break;
|
|
case 'q':
|
|
*p1++='\"';
|
|
p2++;
|
|
break;
|
|
case 'r':
|
|
*p1++='\r';
|
|
p2++;
|
|
break;
|
|
case 't':
|
|
*p1++='\t';
|
|
p2++;
|
|
break;
|
|
case 'v':
|
|
*p1++='\v';
|
|
p2++;
|
|
break;
|
|
case '&':
|
|
p2++;
|
|
if(isxdigit((unsigned char)*p2) && isxdigit((unsigned char)p2[1])){
|
|
i=0;
|
|
i = (i << 4) | ((mytoupper(*p2) >= 'A') ? mytoupper(*p2) - 'A' + 10 : *p2 - '0');
|
|
p++;
|
|
i = (i << 4) | ((mytoupper(*p2) >= 'A') ? mytoupper(*p2) - 'A' + 10 : *p2 - '0');
|
|
if(i==0)error("Null character \\&00 in escape sequence - use CHR$(0)","$");
|
|
p2++;
|
|
*p1++=i;
|
|
} else *p1++='x';
|
|
break;
|
|
default:
|
|
*p1++=*p2++;
|
|
}
|
|
}
|
|
toggle=0;
|
|
} else *p1++ = *p2++;
|
|
}
|
|
} else { // else if not quoted
|
|
for(len = 0, p1 = vtbl[vidx], p2 = (char *)argv[NextData]; *p2 && *p2 != '\'' ; len++, p1++, p2++) {
|
|
if(*p2 < 0x20 || *p2 >= 0x7f) error("Invalid character");
|
|
*p1 = *p2; // copy up to the comma
|
|
}
|
|
}
|
|
if(len > vsize[vidx]) error("String too long");
|
|
*p1 = 0; // terminate the string
|
|
CtoM((unsigned char *)vtbl[vidx]); // convert to a MMBasic string
|
|
}
|
|
else if(vtype[vidx] & T_INT)
|
|
*((long long int *)vtbl[vidx]) = getinteger(argv[NextData]); // much easier if integer variable
|
|
else
|
|
*((MMFLOAT *)vtbl[vidx]) = getnumber(argv[NextData]); // same for numeric variable
|
|
|
|
vidx++;
|
|
NextData += 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmd_call(void){
|
|
int i;
|
|
unsigned char *p=getCstring(cmdline); //get the command we want to call
|
|
unsigned char *q = skipexpression(cmdline);
|
|
if(*q==',')q++;
|
|
i = FindSubFun(p, false); // it could be a defined command
|
|
strcat((char *)p," ");
|
|
strcat((char *)p,(char *)q);
|
|
if(i >= 0) { // >= 0 means it is a user defined command
|
|
DefinedSubFun(false, p, i, NULL, NULL, NULL, NULL);
|
|
}
|
|
else
|
|
error("Unknown user subroutine");
|
|
}
|
|
|
|
|
|
|
|
void MIPS16 cmd_restore(void) {
|
|
if(*cmdline == 0 || *cmdline == '\'') {
|
|
if(CurrentLinePtr >= ProgMemory && CurrentLinePtr < ProgMemory + MAX_PROG_SIZE )
|
|
NextDataLine = ProgMemory;
|
|
else
|
|
NextDataLine = LibMemory;
|
|
NextData = 0;
|
|
} else {
|
|
skipspace(cmdline);
|
|
if(*cmdline=='"') {
|
|
NextDataLine = findlabel(getCstring(cmdline));
|
|
NextData = 0;
|
|
}
|
|
else if(isdigit(*cmdline) || *cmdline==GetTokenValue((unsigned char *)"+") || *cmdline==GetTokenValue((unsigned char *)"-") || *cmdline=='.'){
|
|
NextDataLine = findline(getinteger(cmdline), true); // try for a line number
|
|
NextData = 0;
|
|
} else {
|
|
void *ptr=findvar(cmdline,V_NOFIND_NULL);
|
|
if(ptr){
|
|
if(g_vartbl[g_VarIndex].type & T_NBR) {
|
|
if(g_vartbl[g_VarIndex].dims[0] > 0) { // Not an array
|
|
error("Syntax");
|
|
}
|
|
NextDataLine = findline(getinteger(cmdline), true);
|
|
} else if(g_vartbl[g_VarIndex].type & T_INT) {
|
|
if(g_vartbl[g_VarIndex].dims[0] > 0) { // Not an array
|
|
error("Syntax");
|
|
}
|
|
NextDataLine = findline(getinteger(cmdline), true);
|
|
} else {
|
|
NextDataLine = findlabel(getCstring(cmdline)); // must be a label
|
|
}
|
|
} else if(isnamestart(*cmdline)) {
|
|
NextDataLine = findlabel(cmdline); // must be a label
|
|
}
|
|
NextData = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void cmd_lineinput(void) {
|
|
unsigned char *vp;
|
|
int i, fnbr;
|
|
getargs(&cmdline, 3, (unsigned char *)",;"); // this is a macro and must be the first executable stmt
|
|
if(argc == 0 || argc == 2) error("Syntax");
|
|
|
|
i = 0;
|
|
fnbr = 0;
|
|
if(argc == 3) {
|
|
// is the first argument a file number specifier? If so, get it
|
|
if(*argv[0] == '#' && *argv[1] == ',') {
|
|
argv[0]++;
|
|
fnbr = getinteger(argv[0]);
|
|
}
|
|
else {
|
|
// is the first argument a prompt? if so, print it otherwise there are too many arguments
|
|
if(*argv[1] != ',' && *argv[1] != ';') error("Syntax");
|
|
MMfputs((unsigned char *)getstring(argv[0]), 0);
|
|
}
|
|
i = 2;
|
|
}
|
|
|
|
if(argc - i != 1) error("Syntax");
|
|
vp = findvar(argv[i], V_FIND);
|
|
if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
|
|
if(!(g_vartbl[g_VarIndex].type & T_STR)) error("Invalid variable");
|
|
MMgetline(fnbr, (char *)inpbuf); // get the input line
|
|
if(strlen((char *)inpbuf) > g_vartbl[g_VarIndex].size) error("String too long");
|
|
strcpy((char *)vp, (char *)inpbuf);
|
|
CtoM(vp); // convert to a MMBasic string
|
|
}
|
|
|
|
|
|
void cmd_on(void) {
|
|
int r;
|
|
unsigned char ss[4]; // this will be used to split up the argument line
|
|
unsigned char *p;
|
|
// first check if this is: ON KEY location
|
|
p = checkstring(cmdline, (unsigned char *)"PS2");
|
|
if(p){
|
|
getargs(&p,1,(unsigned char *)",");
|
|
if(*argv[0] == '0' && !isdigit(*(argv[0]+1))){
|
|
OnPS2GOSUB = NULL; // the program wants to turn the interrupt off
|
|
} else {
|
|
OnPS2GOSUB = GetIntAddress(argv[0]); // get a pointer to the interrupt routine
|
|
InterruptUsed = true;
|
|
}
|
|
return;
|
|
}
|
|
p = checkstring(cmdline, (unsigned char *)"KEY");
|
|
if(p) {
|
|
getargs(&p,3,(unsigned char *)",");
|
|
if(argc==1){
|
|
if(*argv[0] == '0' && !isdigit(*(argv[0]+1))){
|
|
OnKeyGOSUB = NULL; // the program wants to turn the interrupt off
|
|
} else {
|
|
OnKeyGOSUB = GetIntAddress(argv[0]); // get a pointer to the interrupt routine
|
|
InterruptUsed = true;
|
|
}
|
|
return;
|
|
} else {
|
|
keyselect=getint(argv[0],0,255);
|
|
if(keyselect==0){
|
|
KeyInterrupt = NULL; // the program wants to turn the interrupt off
|
|
} else {
|
|
if(*argv[2] == '0' && !isdigit(*(argv[2]+1))){
|
|
KeyInterrupt = NULL; // the program wants to turn the interrupt off
|
|
} else {
|
|
KeyInterrupt = (char *)GetIntAddress(argv[2]); // get a pointer to the interrupt routine
|
|
InterruptUsed = true;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
p = checkstring(cmdline, (unsigned char *)"ERROR");
|
|
if(p) {
|
|
if(checkstring(p, (unsigned char *)"ABORT")) {
|
|
OptionErrorSkip = 0;
|
|
return;
|
|
}
|
|
MMerrno = 0; // clear the error flags
|
|
*MMErrMsg = 0;
|
|
if(checkstring(p, (unsigned char *)"CLEAR")) return;
|
|
if(checkstring(p, (unsigned char *)"IGNORE")) {
|
|
OptionErrorSkip = -1;
|
|
return;
|
|
}
|
|
if((p = checkstring(p, (unsigned char *)"SKIP"))) {
|
|
if(*p == 0 || *p == (unsigned char)'\'')
|
|
OptionErrorSkip = 2;
|
|
else
|
|
OptionErrorSkip = getint(p, 1, 10000) + 1;
|
|
return;
|
|
}
|
|
error("Syntax");
|
|
}
|
|
|
|
// if we got here the command must be the traditional: ON nbr GOTO|GOSUB line1, line2,... etc
|
|
|
|
ss[0] = tokenGOTO;
|
|
ss[1] = tokenGOSUB;
|
|
ss[2] = ',';
|
|
ss[3] = 0;
|
|
{ // start a new block
|
|
getargs(&cmdline, (MAX_ARG_COUNT * 2) - 1, ss); // getargs macro must be the first executable stmt in a block
|
|
if(argc < 3 || !(*argv[1] == ss[0] || *argv[1] == ss[1])) error("Syntax");
|
|
if(argc%2 == 0) error("Syntax");
|
|
|
|
r = getint(argv[0], 0, 255); // evaluate the expression controlling the statement
|
|
if(r == 0 || r > argc/2) return; // microsoft say that we just go on to the next line
|
|
|
|
if(*argv[1] == ss[1]) {
|
|
// this is a GOSUB, same as a GOTO but we need to first push the return pointer
|
|
if(gosubindex >= MAXGOSUB) error("Too many nested GOSUB");
|
|
errorstack[gosubindex] = CurrentLinePtr;
|
|
gosubstack[gosubindex++] = nextstmt;
|
|
g_LocalIndex++;
|
|
}
|
|
|
|
if(isnamestart(*argv[r*2]))
|
|
nextstmt = findlabel(argv[r*2]); // must be a label
|
|
else
|
|
nextstmt = findline(getinteger(argv[r*2]), true); // try for a line number
|
|
}
|
|
// IgnorePIN = false;
|
|
}
|
|
|
|
/**
|
|
* @cond
|
|
* The following section will be excluded from the documentation.
|
|
*/
|
|
// utility routine used by DoDim() below and other places in the interpreter
|
|
// checks if the type has been explicitly specified as in DIM FLOAT A, B, ... etc
|
|
unsigned char *CheckIfTypeSpecified(unsigned char *p, int *type, int AllowDefaultType) {
|
|
unsigned char *tp;
|
|
|
|
if((tp = checkstring(p, (unsigned char *)"INTEGER")) != NULL)
|
|
*type = T_INT | T_IMPLIED;
|
|
else if((tp = checkstring(p, (unsigned char *)"STRING")) != NULL)
|
|
*type = T_STR | T_IMPLIED;
|
|
else if((tp = checkstring(p, (unsigned char *)"FLOAT")) != NULL)
|
|
*type = T_NBR | T_IMPLIED;
|
|
else {
|
|
if(!AllowDefaultType) error("Variable type");
|
|
tp = p;
|
|
*type = DefaultType; // if the type is not specified use the default
|
|
}
|
|
return tp;
|
|
}
|
|
|
|
|
|
|
|
unsigned char *SetValue(unsigned char *p, int t, void *v) {
|
|
MMFLOAT f;
|
|
long long int i64;
|
|
unsigned char *s;
|
|
char TempCurrentSubFunName[MAXVARLEN + 1];
|
|
strcpy(TempCurrentSubFunName, (char *)CurrentSubFunName); // save the current sub/fun name
|
|
if(t & T_STR) {
|
|
p = evaluate(p, &f, &i64, &s, &t, true);
|
|
Mstrcpy(v, s);
|
|
}
|
|
else if(t & T_NBR) {
|
|
p = evaluate(p, &f, &i64, &s, &t, false);
|
|
if(t & T_NBR)
|
|
(*(MMFLOAT *)v) = f;
|
|
else
|
|
(*(MMFLOAT *)v) = (MMFLOAT)i64;
|
|
} else {
|
|
p = evaluate(p, &f, &i64, &s, &t, false);
|
|
if(t & T_INT)
|
|
(*(long long int *)v) = i64;
|
|
else
|
|
(*(long long int *)v) = FloatToInt64(f);
|
|
}
|
|
strcpy((char *)CurrentSubFunName, TempCurrentSubFunName); // restore the current sub/fun name
|
|
return p;
|
|
}
|
|
|
|
/** @endcond */
|
|
|
|
|
|
// define a variable
|
|
// DIM [AS INTEGER|FLOAT|STRING] var[(d1 [,d2,...]] [AS INTEGER|FLOAT|STRING] [, ..., ...]
|
|
// LOCAL also uses this function the routines only differ in that LOCAL can only be used in a sub/fun
|
|
void MIPS16 cmd_dim(void) {
|
|
int i, j, k, type, typeSave, ImpliedType = 0, VIndexSave, StaticVar = false;
|
|
unsigned char *p, chSave, *chPosit;
|
|
unsigned char VarName[(MAXVARLEN * 2) + 1];
|
|
void *v, *tv;
|
|
|
|
if(*cmdline == tokenAS) cmdline++; // this means that we can use DIM AS INTEGER a, b, etc
|
|
p = CheckIfTypeSpecified(cmdline, &type, true); // check for DIM FLOAT A, B, ...
|
|
ImpliedType = type;
|
|
{ // getargs macro must be the first executable stmt in a block
|
|
getargs(&p, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)",");
|
|
if((argc & 0x01) == 0) error("Syntax");
|
|
|
|
for(i = 0; i < argc; i += 2) {
|
|
p = skipvar(argv[i], false); // point to after the variable
|
|
while(!(*p == 0 || *p == tokenAS || *p == (unsigned char)'\'' || *p == tokenEQUAL))
|
|
p++; // skip over a LENGTH keyword if there and see if we can find "AS"
|
|
chSave = *p; chPosit = p; *p = 0; // save the char then terminate the string so that LENGTH is evaluated correctly
|
|
if(chSave == tokenAS) { // are we using Microsoft syntax (eg, AS INTEGER)?
|
|
if(ImpliedType & T_IMPLIED) error("Type specified twice");
|
|
p++; // step over the AS token
|
|
p = CheckIfTypeSpecified(p, &type, true); // and get the type
|
|
if(!(type & T_IMPLIED)) error("Variable type");
|
|
}
|
|
|
|
if(cmdtoken == cmdLOCAL) {
|
|
if(g_LocalIndex == 0) error("Invalid here");
|
|
type |= V_LOCAL; // local if defined in a sub/fun
|
|
}
|
|
|
|
if(cmdtoken == cmdSTATIC) {
|
|
if(g_LocalIndex == 0) error("Invalid here");
|
|
// create a unique global name
|
|
if(*CurrentInterruptName)
|
|
strcpy((char *)VarName, CurrentInterruptName); // we must be in an interrupt sub
|
|
else
|
|
strcpy((char *)VarName, CurrentSubFunName); // normal sub/fun
|
|
for(k = 1; k <= MAXVARLEN; k++) if(!isnamechar(VarName[k])) {
|
|
VarName[k] = 0; // terminate the string on a non valid char
|
|
break;
|
|
}
|
|
strcat((char *)VarName,".");
|
|
strcat((char *)VarName, (char *)argv[i]); // by prefixing the var name with the sub/fun name
|
|
StaticVar = true;
|
|
} else
|
|
strcpy((char *)VarName, (char *)argv[i]);
|
|
|
|
v = findvar(VarName, type | V_NOFIND_NULL); // check if the variable exists
|
|
typeSave = type;
|
|
VIndexSave = g_VarIndex;
|
|
if(v == NULL) { // if not found
|
|
v = findvar(VarName, type | V_FIND | V_DIM_VAR); // create the variable
|
|
type = TypeMask(g_vartbl[g_VarIndex].type);
|
|
VIndexSave = g_VarIndex;
|
|
*chPosit = chSave; // restore the char previously removed
|
|
if(g_vartbl[g_VarIndex].dims[0] == -1) error("Array dimensions");
|
|
if(g_vartbl[g_VarIndex].dims[0] > 0) {
|
|
g_DimUsed = true; // prevent OPTION BASE from being used
|
|
v = g_vartbl[g_VarIndex].val.s;
|
|
}
|
|
while(*p && *p != '\'' && tokenfunction(*p) != op_equal) p++; // search through the line looking for the equals sign
|
|
if(tokenfunction(*p) == op_equal) {
|
|
p++; // step over the equals sign
|
|
skipspace(p);
|
|
if(g_vartbl[g_VarIndex].dims[0] > 0 && *p == '(') {
|
|
// calculate the overall size of the array
|
|
for(j = 1, k = 0; k < MAXDIM && g_vartbl[VIndexSave].dims[k]; k++) {
|
|
j *= (g_vartbl[VIndexSave].dims[k] + 1 - g_OptionBase);
|
|
}
|
|
do {
|
|
p++; // step over the opening bracket or terminating comma
|
|
p = SetValue(p, type, v);
|
|
if(type & T_STR) v = (char *)v + g_vartbl[VIndexSave].size + 1;
|
|
if(type & T_NBR) v = (char *)v + sizeof(MMFLOAT);
|
|
if(type & T_INT) v = (char *)v + sizeof(long long int);
|
|
skipspace(p); j--;
|
|
} while(j > 0 && *p == ',');
|
|
if(*p != ')') error("Number of initialising values");
|
|
if(j != 0) error("Number of initialising values");
|
|
} else
|
|
SetValue(p, type, v);
|
|
}
|
|
type = ImpliedType;
|
|
} else {
|
|
if(!StaticVar) error("$ already declared", VarName);
|
|
}
|
|
|
|
|
|
// if it is a STATIC var create a local var pointing to the global var
|
|
if(StaticVar) {
|
|
tv = findvar(argv[i], typeSave | V_LOCAL | V_NOFIND_NULL); // check if the local variable exists
|
|
if(tv != NULL) error("$ already declared", argv[i]);
|
|
tv = findvar(argv[i], typeSave | V_LOCAL | V_FIND | V_DIM_VAR); // create the variable
|
|
if(g_vartbl[VIndexSave].dims[0] > 0 || (g_vartbl[VIndexSave].type & T_STR)) {
|
|
FreeMemory(tv); // we don't need the memory allocated to the local
|
|
g_vartbl[g_VarIndex].val.s = g_vartbl[VIndexSave].val.s; // point to the memory of the global variable
|
|
} else
|
|
g_vartbl[g_VarIndex].val.ia = &(g_vartbl[VIndexSave].val.i); // point to the data of the variable
|
|
g_vartbl[g_VarIndex].type = g_vartbl[VIndexSave].type | T_PTR; // set the type to a pointer
|
|
g_vartbl[g_VarIndex].size = g_vartbl[VIndexSave].size; // just in case it is a string copy the size
|
|
for(j = 0; j < MAXDIM; j++) g_vartbl[g_VarIndex].dims[j] = g_vartbl[VIndexSave].dims[j]; // just in case it is an array copy the dimensions
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void cmd_const(void) {
|
|
unsigned char *p;
|
|
void *v;
|
|
int i, type;
|
|
|
|
getargs(&cmdline, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)","); // getargs macro must be the first executable stmt in a block
|
|
if((argc & 0x01) == 0) error("Syntax");
|
|
|
|
for(i = 0; i < argc; i += 2) {
|
|
p = skipvar(argv[i], false); // point to after the variable
|
|
skipspace(p);
|
|
if(tokenfunction(*p) != op_equal) error("Syntax"); // must be followed by an equals sign
|
|
p++; // step over the equals sign
|
|
type = T_NOTYPE;
|
|
v = DoExpression(p, &type); // evaluate the constant's value
|
|
type = TypeMask(type);
|
|
type |= V_FIND | V_DIM_VAR | T_CONST | T_IMPLIED;
|
|
if(g_LocalIndex != 0) type |= V_LOCAL; // local if defined in a sub/fun
|
|
findvar(argv[i], type); // create the variable
|
|
if(g_vartbl[g_VarIndex].dims[0] != 0) error("Invalid constant");
|
|
if(TypeMask(g_vartbl[g_VarIndex].type) != TypeMask(type)) error("Invalid constant");
|
|
else {
|
|
if(type & T_NBR) g_vartbl[g_VarIndex].val.f = *(MMFLOAT *)v; // and set its value
|
|
if(type & T_INT) g_vartbl[g_VarIndex].val.i = *(long long int *)v;
|
|
if(type & T_STR) {
|
|
if((unsigned char)*(unsigned char *)v<(MAXDIM-1)*sizeof(g_vartbl[g_VarIndex].dims[1])){
|
|
FreeMemorySafe((void **)&g_vartbl[g_VarIndex].val.s);
|
|
g_vartbl[g_VarIndex].val.s=(void *)&g_vartbl[g_VarIndex].dims[1];
|
|
}
|
|
Mstrcpy((unsigned char *)g_vartbl[g_VarIndex].val.s, (unsigned char *)v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @cond
|
|
* The following section will be excluded from the documentation.
|
|
*/
|
|
|
|
// utility function used by llist() below
|
|
// it copys a command or function honouring the case selected by the user
|
|
void strCopyWithCase(char *d, char *s) {
|
|
if(Option.Listcase == CONFIG_LOWER) {
|
|
while(*s) *d++ = tolower(*s++);
|
|
} else if(Option.Listcase == CONFIG_UPPER) {
|
|
while(*s) *d++ = mytoupper(*s++);
|
|
} else {
|
|
while(*s) *d++ = *s++;
|
|
}
|
|
*d = 0;
|
|
}
|
|
|
|
void replaceAlpha(char *str, const char *replacements[MMEND]){
|
|
char buffer[STRINGSIZE]; // Buffer to store the modified string
|
|
int bufferIndex = 0;
|
|
int len = strlen(str);
|
|
int i = 0;
|
|
|
|
while (i < len) {
|
|
// Check for the pattern "~(X)" where X is an uppercase letter
|
|
if (i<len-3 && str[i] == '~' && str[i + 1] == '(' && isupper((int)str[i + 2]) && str[i + 3] == ')') {
|
|
char alpha = str[i + 2]; // Extract the letter 'alpha'
|
|
const char *replacement = replacements[alpha - 'A']; // Get the replacement string
|
|
|
|
// Copy the replacement string into the buffer
|
|
strcpy(&buffer[bufferIndex], replacement);
|
|
bufferIndex += strlen(replacement);
|
|
|
|
i += 4; // Move past "~(X)"
|
|
} else {
|
|
// Copy the current character to the buffer
|
|
buffer[bufferIndex++] = str[i++];
|
|
}
|
|
}
|
|
|
|
buffer[bufferIndex] = '\0'; // Null-terminate the buffer
|
|
strcpy(str, buffer); // Copy the buffer back into the original string
|
|
}
|
|
int format_string(char *c, int n) {
|
|
int count=0;
|
|
n-=2;
|
|
int len = strlen(c);
|
|
if(*c==0)return 0;
|
|
char *result = GetMemory(len * 2); // Allocate enough space for the modified string
|
|
int pos = 0, start = 0;
|
|
|
|
while (start < len) {
|
|
int split_pos = start + n - 1;
|
|
|
|
if (split_pos >= len) { // If remaining text fits in one line
|
|
strcpy(result + pos, c + start);
|
|
pos += strlen(c + start);
|
|
break;
|
|
}
|
|
|
|
while (split_pos > start && !(c[split_pos] == ' ' || c[split_pos] == ',')) {
|
|
split_pos--; // Try to find a space to break at
|
|
}
|
|
|
|
if (split_pos == start) {
|
|
split_pos = start + n - 1; // No space found, force a split
|
|
}
|
|
|
|
strncpy(result + pos, c + start, split_pos - start + 1);
|
|
pos += (split_pos - start + 1);
|
|
|
|
start = split_pos + 1;
|
|
|
|
if (start < len) { // Only add underscore if not the last substring
|
|
result[pos++] = ' ';
|
|
result[pos++] = Option.continuation;
|
|
result[pos++] = '\n';
|
|
count++;
|
|
}
|
|
}
|
|
|
|
result[pos] = '\0';
|
|
strcpy(c,result);
|
|
FreeMemory((void *)result);
|
|
return count;
|
|
}
|
|
// list a line into a buffer (b) given a pointer to the beginning of the line (p).
|
|
// the returned string is a C style string (terminated with a zero)
|
|
// this is used by cmd_list(), cmd_edit() and cmd_xmodem()
|
|
unsigned char *llist(unsigned char *b, unsigned char *p) {
|
|
int i, firstnonwhite = true;
|
|
unsigned char *b_start = b;
|
|
|
|
while(1) {
|
|
if(*p == T_NEWLINE) {
|
|
p++;
|
|
firstnonwhite = true;
|
|
continue;
|
|
}
|
|
|
|
if(*p == T_LINENBR) {
|
|
i = (((p[1]) << 8) | (p[2])); // get the line number
|
|
p += 3; // and step over the number
|
|
IntToStr((char *)b, i, 10);
|
|
b += strlen((char *)b);
|
|
if(*p != ' ') *b++ = ' ';
|
|
}
|
|
|
|
if(*p == T_LABEL) { // got a label
|
|
for(i = p[1], p += 2; i > 0; i--)
|
|
*b++ = *p++; // copy to the buffer
|
|
*b++ = ':'; // terminate with a colon
|
|
if(*p && *p != ' ') *b++ = ' '; // and a space if necessary
|
|
firstnonwhite = true;
|
|
} // this deliberately drops through in case the label is the only thing on the line
|
|
|
|
if(*p >= C_BASETOKEN) {
|
|
if(firstnonwhite) {
|
|
CommandToken tkn=commandtbl_decode(p);
|
|
if(tkn == GetCommandValue( (unsigned char *)"Let"))
|
|
*b = 0; // use nothing if it LET
|
|
else {
|
|
strCopyWithCase((char *)b, (char *)commandname(tkn)); // expand the command (if it is not LET)
|
|
if(*b=='_'){
|
|
if(!strncasecmp((char *)&b[1],"SIDE SET",8) ||
|
|
!strncasecmp((char *)&b[1],"END PROGRAM",11) ||
|
|
!strncasecmp((char *)&b[1],"WRAP",4) ||
|
|
!strncasecmp((char *)&b[1],"LINE",4) ||
|
|
!strncasecmp((char *)&b[1],"PROGRAM",7) ||
|
|
!strncasecmp((char *)&b[1],"LABEL",5)
|
|
) *b='.';
|
|
else if(b[1]=='(')*b='&';
|
|
}
|
|
b += strlen((char *)b); // update pointer to the end of the buffer
|
|
if(isalpha(*(b - 1))) *b++ = ' '; // add a space to the end of the command name
|
|
}
|
|
firstnonwhite = false;
|
|
p+=sizeof(CommandToken);
|
|
} else { // not a command so must be a token
|
|
strCopyWithCase((char *)b, (char *)tokenname(*p)); // expand the token
|
|
b += strlen((char *)b); // update pointer to the end of the buffer
|
|
if(*p == tokenTHEN || *p == tokenELSE)
|
|
firstnonwhite = true;
|
|
else
|
|
firstnonwhite = false;
|
|
p++;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// hey, an ordinary char, just copy it to the output
|
|
if(*p) {
|
|
*b = *p; // place the char in the buffer
|
|
if(*p != ' ') firstnonwhite = false;
|
|
p++; b++; // move the pointers
|
|
continue;
|
|
}
|
|
|
|
// at this point the char must be a zero
|
|
// zero char can mean both a separator or end of line
|
|
if(!(p[1] == T_NEWLINE || p[1] == 0)) {
|
|
*b++ = ':'; // just a separator
|
|
firstnonwhite = true;
|
|
p++;
|
|
continue;
|
|
}
|
|
|
|
// must be the end of a line - so return to the caller
|
|
while(*(b-1) == ' ' && b > b_start) --b; // eat any spaces on the end of the line
|
|
*b = 0;
|
|
replaceAlpha((char *)b_start, overlaid_functions) ; //replace the user version of all the MM. functions
|
|
STR_REPLACE((char *)b_start, "PEEK(INT8", "PEEK(BYTE",0);
|
|
return ++p;
|
|
} // end while
|
|
}
|
|
|
|
|
|
|
|
void execute_one_command(unsigned char *p) {
|
|
int i;
|
|
|
|
CheckAbort();
|
|
targ = T_CMD;
|
|
skipspace(p); // skip any whitespace
|
|
if(p[0]>= C_BASETOKEN && p[1]>=C_BASETOKEN){
|
|
// if(*(char*)p >= C_BASETOKEN && *(char*)p - C_BASETOKEN < CommandTableSize - 1 && (commandtbl[*(char*)p - C_BASETOKEN].type & T_CMD)) {
|
|
CommandToken cmd=commandtbl_decode(p);
|
|
if(cmd == cmdWHILE || cmd == cmdDO || cmd == cmdFOR) error("Invalid inside THEN ... ELSE") ;
|
|
cmdtoken=cmd;
|
|
cmdline = p + sizeof(CommandToken);
|
|
skipspace(cmdline);
|
|
commandtbl[cmd].fptr(); // execute the command
|
|
} else {
|
|
if(!isnamestart(*p)) error("Invalid character");
|
|
i = FindSubFun(p, false); // it could be a defined command
|
|
if(i >= 0) // >= 0 means it is a user defined command
|
|
DefinedSubFun(false, p, i, NULL, NULL, NULL, NULL);
|
|
else
|
|
error("Unknown command");
|
|
}
|
|
ClearTempMemory(); // at the end of each command we need to clear any temporary string vars
|
|
}
|
|
|
|
void execute(char* mycmd) {
|
|
// char *temp_tknbuf;
|
|
unsigned char* ttp=NULL;
|
|
int i = 0, toggle = 0;
|
|
// temp_tknbuf = GetTempStrMemory();
|
|
// strcpy(temp_tknbuf, tknbuf);
|
|
// first save the current token buffer in case we are in immediate mode
|
|
// we have to fool the tokeniser into thinking that it is processing a program line entered at the console
|
|
skipspace(mycmd);
|
|
strcpy((char *)inpbuf, (const char *)getCstring((unsigned char *)mycmd)); // then copy the argument
|
|
if (!(toupper(inpbuf[0]) == 'R' && toupper(inpbuf[1]) == 'U' && toupper(inpbuf[2]) == 'N')) { //convert the string to upper case
|
|
while (inpbuf[i]) {
|
|
if (inpbuf[i] == 34) {
|
|
if (toggle == 0)toggle = 1;
|
|
else toggle = 0;
|
|
}
|
|
if (!toggle) {
|
|
if (inpbuf[i] == ':')error((char *)"Only single statements allowed");
|
|
inpbuf[i] = toupper(inpbuf[i]);
|
|
}
|
|
i++;
|
|
}
|
|
multi=false;
|
|
tokenise(true); // and tokenise it (the result is in tknbuf)
|
|
memset(inpbuf, 0, STRINGSIZE);
|
|
tknbuf[strlen((char *)tknbuf)] = 0;
|
|
tknbuf[strlen((char*)tknbuf) + 1] = 0;
|
|
if(CurrentLinePtr)ttp = nextstmt; // save the globals used by commands
|
|
ScrewUpTimer = 1000;
|
|
ExecuteProgram(tknbuf); // execute the function's code
|
|
ScrewUpTimer = 0;
|
|
// g_TempMemoryIsChanged = true; // signal that temporary memory should be checked
|
|
if(CurrentLinePtr)nextstmt = ttp;
|
|
return;
|
|
}
|
|
else {
|
|
unsigned char* p = inpbuf;
|
|
// char* q;
|
|
// char fn[STRINGSIZE] = { 0 };
|
|
unsigned short tkn=GetCommandValue((unsigned char *)"RUN");
|
|
tknbuf[0] = (tkn & 0x7f ) + C_BASETOKEN;
|
|
tknbuf[1] = (tkn >> 7) + C_BASETOKEN; //tokens can be 14-bit
|
|
p[0] = (tkn & 0x7f ) + C_BASETOKEN;
|
|
p[1] = (tkn >> 7) + C_BASETOKEN; //tokens can be 14-bit
|
|
memmove(&p[2], &p[4], strlen((char *)p) - 4);
|
|
/* if ((q = strchr((char *)p, ':'))) {
|
|
q--;
|
|
*q = '0';
|
|
}*/
|
|
p[strlen((char*)p) - 2] = 0;
|
|
// MMPrintString(fn); PRet();
|
|
// CloseAudio(1);
|
|
strcpy((char *)tknbuf, (char*)inpbuf);
|
|
if (CurrentlyPlaying != P_NOTHING)CloseAudio(1);
|
|
longjmp(jmprun, 1);
|
|
}
|
|
}
|
|
/** @endcond */
|
|
|
|
void cmd_execute(void) {
|
|
execute((char*)cmdline);
|
|
}
|
|
|
|
|