2025-06-15 21:31:45 +08:00

1371 lines
45 KiB
C

/***********************************************************************************************************************
PicoMite MMBasic
Functions.c
<COPYRIGHT HOLDERS> 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 Functions.c
* @author Geoff Graham, Peter Mather
* @brief Source for standard MMBasic commands
*/
/**
* @cond
* The following section will be excluded from the documentation.
*/
#include <math.h>
#include "stdlib.h"
#include "MMBasic_Includes.h"
#include "Hardware_Includes.h"
#include <float.h>
#include "xregex.h"
#ifdef rp2350
#include "pico/rand.h"
#endif
extern long long int llabs (long long int n);
const char* overlaid_functions[]={
"MM.HRES",
"MM.VRES",
"MM.VER",
"MM.I2C",
"MM.FONTHEIGHT",
"MM.FONTWIDTH",
#ifndef USBKEYBOARD
"MM.PS2",
#endif
"MM.HPOS",
"MM.VPOS",
"MM.ONEWIRE",
"MM.Errno",
"MM.ErrMsg$",
"MM.WATCHDOG",
"MM.DEVICE$",
"MM.CMDLINE$",
#ifdef PICOMITEWEB
"MM.MESSAGE$",
"MM.ADDRESS$",
"MM.TOPIC$",
#endif
"MM.FLAGS",
"MM.DISPLAY",
"MM.WIDTH",
"MM.HEIGHT",
"MM.PERSISTENT",
"MM.END"
};
#ifndef rp2350
const MMFLOAT sinetab[360]={
0.000000000000000,0.017452406437284,0.034899496702501,0.052335956242944,0.069756473744125,0.087155742747658,0.104528463267653,0.121869343405147,0.139173100960065,0.156434465040231,
0.173648177666930,0.190808995376545,0.207911690817759,0.224951054343865,0.241921895599668,0.258819045102521,0.275637355816999,0.292371704722737,0.309016994374947,0.325568154457157,
0.342020143325669,0.358367949545300,0.374606593415912,0.390731128489274,0.406736643075800,0.422618261740699,0.438371146789077,0.453990499739547,0.469471562785891,0.484809620246337,
0.500000000000000,0.515038074910054,0.529919264233205,0.544639035015027,0.559192903470747,0.573576436351046,0.587785252292473,0.601815023152048,0.615661475325658,0.629320391049837,
0.642787609686539,0.656059028990507,0.669130606358858,0.681998360062499,0.694658370458997,0.707106781186547,0.719339800338651,0.731353701619171,0.743144825477394,0.754709580222772,
0.766044443118978,0.777145961456971,0.788010753606722,0.798635510047293,0.809016994374947,0.819152044288992,0.829037572555042,0.838670567945424,0.848048096156426,0.857167300702112,
0.866025403784439,0.874619707139396,0.882947592858927,0.891006524188368,0.898794046299167,0.906307787036650,0.913545457642601,0.920504853452440,0.927183854566787,0.933580426497202,
0.939692620785908,0.945518575599317,0.951056516295153,0.956304755963035,0.961261695938319,0.965925826289068,0.970295726275997,0.974370064785235,0.978147600733806,0.981627183447664,
0.984807753012208,0.987688340595138,0.990268068741570,0.992546151641322,0.994521895368273,0.996194698091746,0.997564050259824,0.998629534754574,0.999390827019096,0.999847695156391,
1.000000000000000,0.999847695156391,0.999390827019096,0.998629534754574,0.997564050259824,0.996194698091746,0.994521895368273,0.992546151641322,0.990268068741570,0.987688340595138,
0.984807753012208,0.981627183447664,0.978147600733806,0.974370064785235,0.970295726275997,0.965925826289069,0.961261695938319,0.956304755963036,0.951056516295153,0.945518575599317,
0.939692620785909,0.933580426497202,0.927183854566787,0.920504853452440,0.913545457642601,0.906307787036650,0.898794046299167,0.891006524188368,0.882947592858927,0.874619707139396,
0.866025403784439,0.857167300702112,0.848048096156426,0.838670567945424,0.829037572555042,0.819152044288992,0.809016994374948,0.798635510047293,0.788010753606722,0.777145961456971,
0.766044443118978,0.754709580222772,0.743144825477394,0.731353701619171,0.719339800338651,0.707106781186548,0.694658370458998,0.681998360062499,0.669130606358858,0.656059028990507,
0.642787609686540,0.629320391049837,0.615661475325658,0.601815023152048,0.587785252292473,0.573576436351046,0.559192903470747,0.544639035015027,0.529919264233205,0.515038074910054,
0.500000000000000,0.484809620246337,0.469471562785891,0.453990499739547,0.438371146789078,0.422618261740699,0.406736643075800,0.390731128489274,0.374606593415912,0.358367949545300,
0.342020143325669,0.325568154457157,0.309016994374947,0.292371704722737,0.275637355816999,0.258819045102521,0.241921895599668,0.224951054343865,0.207911690817759,0.190808995376545,
0.173648177666930,0.156434465040231,0.139173100960066,0.121869343405148,0.104528463267654,0.087155742747658,0.069756473744126,0.052335956242944,0.034899496702501,0.017452406437283,
0.000000000000000,-0.017452406437283,-0.034899496702501,-0.052335956242944,-0.069756473744125,-0.087155742747658,-0.104528463267653,-0.121869343405147,-0.139173100960066,-0.156434465040231,
-0.173648177666930,-0.190808995376545,-0.207911690817759,-0.224951054343865,-0.241921895599667,-0.258819045102521,-0.275637355816999,-0.292371704722737,-0.309016994374947,-0.325568154457156,
-0.342020143325669,-0.358367949545300,-0.374606593415912,-0.390731128489274,-0.406736643075800,-0.422618261740699,-0.438371146789077,-0.453990499739547,-0.469471562785891,-0.484809620246337,
-0.500000000000000,-0.515038074910054,-0.529919264233205,-0.544639035015027,-0.559192903470747,-0.573576436351046,-0.587785252292473,-0.601815023152048,-0.615661475325658,-0.629320391049837,
-0.642787609686539,-0.656059028990507,-0.669130606358858,-0.681998360062498,-0.694658370458997,-0.707106781186547,-0.719339800338651,-0.731353701619171,-0.743144825477394,-0.754709580222772,
-0.766044443118978,-0.777145961456971,-0.788010753606722,-0.798635510047293,-0.809016994374947,-0.819152044288992,-0.829037572555041,-0.838670567945424,-0.848048096156426,-0.857167300702112,
-0.866025403784438,-0.874619707139396,-0.882947592858927,-0.891006524188368,-0.898794046299167,-0.906307787036650,-0.913545457642601,-0.920504853452440,-0.927183854566787,-0.933580426497202,
-0.939692620785909,-0.945518575599317,-0.951056516295153,-0.956304755963035,-0.961261695938319,-0.965925826289069,-0.970295726275996,-0.974370064785235,-0.978147600733806,-0.981627183447664,
-0.984807753012208,-0.987688340595138,-0.990268068741570,-0.992546151641322,-0.994521895368273,-0.996194698091746,-0.997564050259824,-0.998629534754574,-0.999390827019096,-0.999847695156391,
-1.000000000000000,-0.999847695156391,-0.999390827019096,-0.998629534754574,-0.997564050259824,-0.996194698091746,-0.994521895368274,-0.992546151641322,-0.990268068741570,-0.987688340595138,
-0.984807753012208,-0.981627183447664,-0.978147600733806,-0.974370064785235,-0.970295726275997,-0.965925826289069,-0.961261695938319,-0.956304755963035,-0.951056516295154,-0.945518575599317,
-0.939692620785909,-0.933580426497202,-0.927183854566787,-0.920504853452441,-0.913545457642601,-0.906307787036650,-0.898794046299167,-0.891006524188368,-0.882947592858927,-0.874619707139396,
-0.866025403784439,-0.857167300702112,-0.848048096156426,-0.838670567945424,-0.829037572555042,-0.819152044288992,-0.809016994374948,-0.798635510047293,-0.788010753606722,-0.777145961456971,
-0.766044443118978,-0.754709580222772,-0.743144825477395,-0.731353701619170,-0.719339800338651,-0.707106781186548,-0.694658370458998,-0.681998360062499,-0.669130606358858,-0.656059028990507,
-0.642787609686540,-0.629320391049838,-0.615661475325658,-0.601815023152048,-0.587785252292473,-0.573576436351047,-0.559192903470747,-0.544639035015027,-0.529919264233205,-0.515038074910055,
-0.500000000000000,-0.484809620246337,-0.469471562785891,-0.453990499739547,-0.438371146789078,-0.422618261740700,-0.406736643075800,-0.390731128489274,-0.374606593415912,-0.358367949545301,
-0.342020143325669,-0.325568154457157,-0.309016994374948,-0.292371704722737,-0.275637355817000,-0.258819045102521,-0.241921895599668,-0.224951054343865,-0.207911690817760,-0.190808995376545,
-0.173648177666930,-0.156434465040231,-0.139173100960066,-0.121869343405148,-0.104528463267653,-0.087155742747658,-0.069756473744126,-0.052335956242944,-0.034899496702501,-0.017452406437284
};
#endif
/********************************************************************************************************************************************
basic functions
each function is responsible for decoding a basic function
all function names are in the form fun_xxxx() so, if you want to search for the function responsible for the ASC() function look for fun_asc
There are 4 globals used by these functions:
unsigned char *ep This is a pointer to the argument of the function
Eg, in the case of INT(35/7) ep would point to "35/7)"
fret Is the return value for a basic function that returns a float
iret Is the return value for a basic function that returns an integer
sret Is the return value for a basic function that returns a string
tret Is the type of the return value. normally this is set by the caller and is not changed by the function
********************************************************************************************************************************************/
/*
DOCUMENTATION
=============
FIELD$( str$, field)
or
FIELD$( str$, field, delim$)
or
FIELD$( str$, field, delim$, quote$)
Extract a substring (ie, field) from 'str$'. Each is separated by any one of
the characters in the string 'delim$' and the field number to return is specified
by 'field' (the first field is field number 1). Any leading and trailing
spaces will be trimmed from the returned string.
Note that 'delim$' can contain a number of characters and the fields
will then be separated by any one of these characters. if delim$ is not
provided it will default to a comma (,) character.
'quote$' is the set of characters that might be used to quote text. Typically
it is the double quote character (") and any text that is surrounded by the quote
character(s) will be treated as a block and any 'delim$' characters within that
block will not be used as delimiters.
This function is useful for splitting apart comma-separated-values (CSV) in data
streams produced by GPS modules and test equipment. For example:
PRINT FIELD$("aaa,bbb,ccc", 2, ",")
Will print the string: bbb
PRINT FIELD$("text1, 'quoted, text', text3", 2, ",", "'")
will print the string: 'quoted, text'
*/
// return true if the char 'c' is contained in the string 'srch$'
// used only by scan_for_delimiter() below
// Note: this operates on MMBasic strings
static int MInStr(char *srch, char c) {
int i;
for(i = 1; i <= *(unsigned char *)srch; i++)
if(c == srch[i])
return true;
return false;
}
/* @endcond */
void fun_bound(void){
int which=1;
getargs(&ep, 3,(unsigned char *)",");
if(argc==3)which=getint(argv[2],0,MAXDIM);
findvar(argv[0], V_FIND | V_EMPTY_OK | V_NOFIND_ERR);
if(which==0)iret=g_OptionBase;
else iret=g_vartbl[g_VarIndex].dims[which-1];
if(iret==-1)iret=0;
targ=T_INT;
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
// scan through string p and return p if it points to any char in delims
// this will skip any quoted text (quote delimiters in quotes)
// used only by fun_field() below
// Note: this operates on MMBasic strings
static int scan_for_delimiter(int start, unsigned char *p, unsigned char *delims, unsigned char *quotes) {
int i;
unsigned char qidx;
for(i = start; i <= *(unsigned char *)p && !MInStr((char *)delims, (char )p[i]); i++) {
if(MInStr((char *)quotes, (char )p[i])) { // if we have a quote
qidx = p[i];
i++; // step over the opening quote
while(i < *(unsigned char *)p && p[i] != qidx) i++; // skip the quoted text
}
}
return i;
}
/* @endcond */
void fun_call(void){
int i;
long long int i64 = 0;
unsigned char *s = NULL;
MMFLOAT f;
unsigned char *q = ep; // store the value of 'ep' because calling getCstring() can change it.
unsigned char *p=getCstring(ep); //get the command we want to call
q = skipexpression(q);
if(*q==',')q++;
i = FindSubFun(p, true); // it could be a defined command
strcat((char *)p," ");
strcat((char *)p,(char *)q);
targ= T_NOTYPE;
if(i >= 0) { // >= 0 means it is a user defined command
DefinedSubFun(true, p, i, &f, &i64, &s, &targ);
} else error("Unknown user function");
if(targ & T_STR) {
sret=GetTempMemory(STRINGSIZE);
Mstrcpy(sret, s); // if it is a string then save it
}
if(targ & T_INT)iret=i64;
if(targ & T_NBR)fret=f;
}
// syntax: str$ = FIELD$(string1, nbr, string2, string3)
// find field nbr in string1 using the delimiters in string2 to separate the fields
// if string3 is present any chars quoted by chars in string3 will not be searched for delimiters
// Note: this operates on MMBasic strings
void fun_field(void) {
unsigned char *p, *delims = (unsigned char *)"\1,", *quotes = (unsigned char *)"\0";
int fnbr, i, j, k;
getargs(&ep, 7,(unsigned char *)",");
if(!(argc == 3 || argc == 5 || argc == 7)) error("Syntax");
p = getstring(argv[0]); // the string containing the fields
fnbr = getint(argv[2], 1, MAXSTRLEN); // field nbr to return
if(argc > 3 && *argv[4]) delims = getstring(argv[4]); // delimiters for fields
if(argc == 7) quotes = getstring(argv[6]); // delimiters for quoted text
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
targ = T_STR;
i = 1;
while(--fnbr > 0) {
i = scan_for_delimiter(i, p, delims, quotes);
if(i > *p) return;
i++; // step over the delimiter
}
while(p[i] == ' ') i++; // trim leading spaces
j = scan_for_delimiter(i, p, delims, quotes); // find the end of the field
*sret = k = j - i;
for(j = 1; j <= k; j++, i++) sret[j] = p[i]; // copy to the return string
for(k = *sret; k > 0 && sret[k] == ' '; k--); // trim trailing spaces
*sret = k;
}
void fun_str2bin(void){
union binmap{
int8_t c[8];
uint8_t uc[8];
float f;
double d;
int64_t l;
uint64_t ul;
int i;
uint32_t ui;
short s;
uint16_t us;
}map;
int j;
getargs(&ep, 5,(unsigned char *)",");
if(!(argc==3 || argc==5))error("Syntax");
if(argc==5 && !checkstring(argv[4],(unsigned char *)"BIG"))error("Syntax");
char *p;
p = (char *)getstring(argv[2]);
int len=p[0];
map.l=0;
for(j=0;j<len;j++)map.c[j]=p[j+1];
if(argc==5){ // big endian so swap byte order
char k;
int m;
for(j=0;j<(len>>1);j++){
m=len-j-1;
k=map.c[j];
map.c[j]=map.c[m];
map.c[m]=k;
}
}
if(checkstring(argv[0],(unsigned char *)"DOUBLE")){
if(len!=8)error("String length");
targ=T_NBR;
fret=(MMFLOAT)map.d;
} else if(checkstring(argv[0],(unsigned char *)"SINGLE")){
if(len!=4)error("String length");
targ=T_NBR;
fret=(MMFLOAT)map.f;
} else if(checkstring(argv[0],(unsigned char *)"INT64")){
if(len!=8)error("String length");
targ=T_INT;
iret=(int64_t)map.l;
} else if( checkstring(argv[0],(unsigned char *)"INT32")){
if(len!=4)error("String length");
targ=T_INT;
iret=(int64_t)map.i;
} else if(checkstring(argv[0],(unsigned char *)"INT16")){
if(len!=2)error("String length");
targ=T_INT;
iret=(int64_t)map.s;
} else if(checkstring(argv[0],(unsigned char *)"INT8")){
if(len!=1)error("String length");
targ=T_INT;
iret=(int64_t)map.c[0];
} else if(checkstring(argv[0],(unsigned char *)"UINT64")){
if(len!=8)error("String length");
targ=T_INT;
iret=(int64_t)map.ul;
} else if(checkstring(argv[0],(unsigned char *)"UINT32")){
if(len!=4)error("String length");
targ=T_INT;
iret=(int64_t)map.ui;
} else if(checkstring(argv[0],(unsigned char *)"UINT16")){
if(len!=2)error("String length");
targ=T_INT;
iret=(int64_t)map.us;
} else if(checkstring(argv[0],(unsigned char *)"UINT8")){
if(len!=1)error("String length");
targ=T_INT;
iret=(int64_t)map.uc[0];
} else error("Syntax");
}
void fun_bin2str(void){
int j, len=0;
union binmap{
int8_t c[8];
uint8_t uc[8];
float f;
double d;
int64_t l;
uint64_t ul;
int i;
uint32_t ui;
short s;
uint16_t us;
}map;
int64_t i64;
getargs(&ep, 5,(unsigned char *)",");
if(!(argc==3 || argc==5))error("Syntax");
if(argc==5 && !(checkstring(argv[4],(unsigned char *)"BIG")))error("Syntax");
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
if(checkstring(argv[0],(unsigned char *)"DOUBLE")){
len=8;
map.d=(double)getnumber(argv[2]);
} else if(checkstring(argv[0],(unsigned char *)"SINGLE")){
len=4;
map.f=(float)getnumber(argv[2]);
} else {
i64=getinteger(argv[2]);
if(checkstring(argv[0],(unsigned char *)"INT64")){
len=8;
map.l=(int64_t)i64;
} else if( checkstring(argv[0],(unsigned char *)"INT32")){
len=4;
if(i64 > 2147483647 || i64 < -2147483648)error("Overflow");
map.i=(int32_t)i64;
} else if(checkstring(argv[0],(unsigned char *)"INT16")){
len=2;
if(i64 > 32767 || i64 < -32768)error("Overflow");
map.s=(int16_t)i64;
} else if(checkstring(argv[0],(unsigned char *)"INT8")){
len=1;
if(i64 > 127 || i64 < -128)error("Overflow");
map.c[0]=(int8_t)i64;
} else if(checkstring(argv[0],(unsigned char *)"UINT64")){
len=8;
map.ul=(uint64_t)i64;
} else if(checkstring(argv[0],(unsigned char *)"UINT32")){
len=4;
if(i64 > 4294967295 || i64 < 0)error("Overflow");
map.ui=(uint32_t)i64;
} else if(checkstring(argv[0],(unsigned char *)"UINT16")){
len=2;
if(i64 > 65535 || i64 < 0)error("Overflow");
map.us=(uint16_t)i64;
} else if(checkstring(argv[0],(unsigned char *)"UINT8")){
len=1;
if(i64 > 255 || i64 < 0)error("Overflow");
map.uc[0]=(uint8_t)i64;
} else error("Syntax");
}
for(j=0;j<len;j++)sret[j]=map.c[j];
if(argc==5){ // big endian so swap byte order
unsigned char k;
int m;
for(j=0;j<(len>>1);j++){
m=len-j-1;
k=sret[j];
sret[j]=sret[m];
sret[m]=k;
}
}
// convert from c type string but it can contain zeroes
unsigned char *p1, *p2;
j=len;
p1 = sret + len; p2 = sret + len - 1;
while(j--) *p1-- = *p2--;
*sret = len;
targ = T_STR;
}
// return the absolute value of a number (ie, without the sign)
// a = ABS(nbr)
void fun_abs(void) {
unsigned char *s;
MMFLOAT f;
long long int i64;
targ = T_INT;
evaluate(ep, &f, &i64, &s, &targ, false); // get the value and type of the argument
if(targ & T_NBR)
fret = fabs(f);
else {
iret = i64;
if(iret < 0) iret = -iret;
}
}
// return the ASCII value of the first character in a string (ie, its number value)
// a = ASC(str$)
void fun_asc(void) {
unsigned char *s;
s = getstring(ep);
if(*s == 0)
iret = 0;
else
iret = *(s + 1);
targ = T_INT;
}
void fun_bit(void){
uint64_t *s;
uint64_t spos;
getargs(&ep, 3, (unsigned char *)",");
s=(uint64_t *)findvar(argv[0], V_NOFIND_ERR);
if(!(g_vartbl[g_VarIndex].type & T_INT)) error("Not an integer");
spos = getint(argv[2], 0,63); // the mid position
iret = ((int64_t)(*s&(1ll<<spos))>>spos) & 1ll;
targ=T_INT;
}
void fun_flag(void){
uint64_t spos;
getargs(&ep, 1, (unsigned char *)",");
spos = getint(argv[0], 0,63); // the mid position
iret = ((int64_t)(g_flag & (1ll<<spos))>>spos) & 1ll;
targ=T_INT;
}
void fun_byte(void){
unsigned char *s;
int spos;
getargs(&ep, 3, (unsigned char *)",");
s=(unsigned char *)findvar(argv[0], V_NOFIND_ERR);
if(!(g_vartbl[g_VarIndex].type & T_STR)) error("Not a string");
spos = getint(argv[2], 1, g_vartbl[g_VarIndex].size); // the mid position
iret = s[spos]; // this will last for the life of the command
targ = T_INT;
}
void fun_tilde(void){
targ=T_INT;
/*
typedef enum {
MMHRES,
MMVRES,
MMVER,
MMI2C,
MMFONTHEIGHT,
MMFONTWIDTH,
#ifndef USBKEYBOARD
MMPS2,
#endif
MMHPOS,
MMVPOS,
MMONEWIRE,
MMERRNO,
MMERRMSG,
MMWATCHDOG,
MMDEVICE,
MMCMDLINE,
#ifdef PICOMITEWEB
MMMESSAGE,
MMADDRESS,
MMTOPIC,
#endif
MMFLAG,
MMDISPLAY,
MMWIDTH,
MMHEIGHT,
MMPERSISTENT,
MMEND
} Operation;
*/
switch(*ep-'A'){
case MMHRES:
iret=HRes;
break;
case MMVRES:
iret=VRes;
break;
case MMVER:
fun_version();
break;
case MMI2C:
iret=mmI2Cvalue;
break;
case MMFONTHEIGHT:
iret = FontTable[gui_font >> 4][1] * (gui_font & 0b1111);
break;
case MMFONTWIDTH:
iret = FontTable[gui_font >> 4][0] * (gui_font & 0b1111);
break;
#ifndef USBKEYBOARD
case MMPS2:
iret = (int64_t)(uint32_t)PS2code;
break;
#endif
case MMHPOS:
iret = CurrentX;
break;
case MMVPOS:
iret = CurrentY;
break;
case MMONEWIRE:
iret = mmOWvalue;
break;
case MMERRNO:
iret = MMerrno;
break;
case MMERRMSG:
fun_errmsg();
break;
case MMWATCHDOG:
iret = WatchdogSet;
break;
case MMDEVICE:
fun_device();
break;
case MMCMDLINE:
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
Mstrcpy(sret,cmdlinebuff);
targ=T_STR;
break;
#ifdef PICOMITEWEB
case MMMESSAGE:
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
Mstrcpy(sret,messagebuff);
targ=T_STR;
break;
case MMADDRESS:
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
Mstrcpy(sret,addressbuff);
targ=T_STR;
break;
case MMTOPIC:
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
Mstrcpy(sret,topicbuff);
targ=T_STR;
break;
#endif
case MMFLAG:
iret=g_flag;
break;
case MMDISPLAY:
iret=Option.DISPLAY_CONSOLE ? 1 : 0;
break;
case MMWIDTH:
iret=HRes/(short)(FontTable[gui_font >> 4][0] * (gui_font & 0b1111));
break;
case MMHEIGHT:
iret=VRes/(short)(FontTable[gui_font >> 4][1] * (gui_font & 0b1111));
break;
case MMPERSISTENT:
iret=_persistent;
break;
default:
iret=-1;
}
}
// return the arctangent of a number in radians
void fun_atn(void) {
fret = useoptionangle ? atan(getnumber(ep))*optionangle : atan(getnumber(ep));
targ = T_NBR;
}
void fun_atan2(void) {
MMFLOAT y,x,z;
getargs(&ep, 3, (unsigned char *)",");
if(argc != 3)error("Syntax");
y=getnumber(argv[0]);
x=getnumber(argv[2]);
z=atan2((float)y,(float)x);
fret=useoptionangle ? z*optionangle: z;
targ = T_NBR;
}
// convert a number into a one character string
// s$ = CHR$(nbr)
void fun_chr(void) {
int i;
i = getint(ep, 0, 0xff);
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
sret[0] = 1;
sret[1] = i;
targ = T_STR;
}
// Round numbers with fractional portions up or down to the next whole number or integer.
void fun_cint(void) {
iret = getinteger(ep);
targ = T_INT;
}
void fun_cos(void) {
if(useoptionangle){
#ifndef rp2350
MMFLOAT t=getnumber(ep);
if (t == (int)t) {
int integerPart = (int)t;
// Modulus 360 and ensure it's in the range [0, 359]
fret= sinetab[(integerPart % 360 + 450) % 360];
}
else
#endif
fret=cos(getnumber(ep)/optionangle);
} else {
fret = cos(getnumber(ep));
}
targ = T_NBR;
}
// convert radians to degrees. Thanks to Alan Williams for the contribution
void fun_deg(void) {
fret = (MMFLOAT)((MMFLOAT)getnumber(ep)*RADCONV);
targ = T_NBR;
}
// Returns the exponential value of a number.
void fun_exp(void) {
fret = exp(getnumber(ep));
targ = T_NBR;
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
// utility function used by HEX$(), OCT$() and BIN$()
void DoHexOctBin(int base) {
unsigned long long int i;
int j = 1;
getargs(&ep, 3, (unsigned char *)",");
i = (unsigned long long int )getinteger(argv[0]); // get the number
if(argc == 3) j = getint(argv[2], 0, MAXSTRLEN); // get the optional number of chars to return
if(j==0)j=1;
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
IntToStrPad((char *)sret, (signed long long int )i, '0', j, base);
CtoM(sret);
targ = T_STR;
}
/* @endcond */
// return the hexadecimal representation of a number
// s$ = HEX$(nbr)
void fun_hex(void) {
DoHexOctBin(16);
}
// return the octal representation of a number
// s$ = OCT$(nbr)
void fun_oct(void) {
DoHexOctBin(8);
}
// return the binary representation of a number
// s$ = BIN$(nbr)
void fun_bin(void) {
DoHexOctBin(2);
}
// syntax: nbr = INSTR([start,] string1, string2)
// find the position of string2 in string1 starting at start chars in string1
// returns an integer
void fun_instr(void) {
unsigned char *s1 = NULL, *s2 = NULL;
int t, start = 0, n = 0 ;
unsigned char *ss;
MMFLOAT f;
long long int i64;
getargs(&ep, 7, (unsigned char *)",");
if(!(argc==3 || argc==5 || argc==7))error("Syntax");
t = T_NOTYPE;
evaluate(argv[0], &f, &i64, &ss, &t, false); // get the value and type of the argument
if(t & T_NBR){
n=2;
start=getint(argv[0],0,255)-1;
} else if(t & T_INT){
n=2;
start=getint(argv[0],0,255)-1;
} else if(t & T_STR){
n=0;
} else error("Syntax");
if(argc < (n==2 ? 7 : 5)){
s1 = getstring(argv[0+n]);
s2 = getstring(argv[2+n]);
targ = T_INT;
if(start > *s1 - *s2 + 1 || *s2 == 0)
iret = 0;
else {
// find s2 in s1 using MMBasic strings
int i;
for(i = start; i < *s1 - *s2 + 1; i++) {
if(memcmp(s1 + i + 1, s2 + 1, *s2) == 0) {
iret = i + 1;
return;
}
}
}
iret = 0;
} else {
regex_t regex;
int reti;
regmatch_t pmatch;
MMFLOAT *temp=NULL;
char *s=GetTempMemory(STRINGSIZE), *p=GetTempMemory(STRINGSIZE);
strcpy(s,(char *)getCstring(argv[0+n]));
strcpy(p,(char *)getCstring(argv[2+n]));
if(argc==5+n){
temp = findvar(argv[4+n], V_FIND);
if(!(g_vartbl[g_VarIndex].type & T_NBR)) error("Invalid variable");
}
reti = regcomp(&regex, p, 0);
if( reti ){
regfree(&regex);
error("Could not compile regex");
}
reti = regexec(&regex, &s[start], 1, &pmatch, 0);
targ=T_INT;
if( !reti ){
iret=pmatch.rm_so+1+start;
if(temp)*temp=(MMFLOAT)(pmatch.rm_eo-pmatch.rm_so);
}
else if( reti == REG_NOMATCH ){
iret=0;
if(temp)*temp=0.0;
}
else{
regfree(&regex);
error("Regex execution error");
}
regfree(&regex);
}
targ=T_INT;
}
// Truncate an expression to the next whole number less than or equal to the argument.
void fun_int(void) {
iret = floor(getnumber(ep));
targ = T_INT;
}
// Truncate a number to a whole number by eliminating the decimal point and all characters
// to the right of the decimal point.
void fun_fix(void) {
iret = getnumber(ep);
targ = T_INT;
}
// Return a substring offset by a number of characters from the left (beginning) of the string.
// s$ = LEFT$( string$, nbr )
void fun_left(void) {
int i;
unsigned char *s;
getargs(&ep, 3, (unsigned char *)",");
if(argc != 3) error("Argument count");
s = GetTempMemory(STRINGSIZE); // this will last for the life of the command
Mstrcpy(s, getstring(argv[0]));
i = getint(argv[2], 0, MAXSTRLEN);
if(i < *s) *s = i; // truncate if it is less than the current string length
sret = s;
targ = T_STR;
}
// Return a substring of ?string$? with ?number-of-chars? from the right (end) of the string.
// s$ = RIGHT$( string$, number-of-chars )
void fun_right(void) {
int nbr;
unsigned char *s, *p1, *p2;
getargs(&ep, 3, (unsigned char *)",");
if(argc != 3) error("Argument count");
s = getstring(argv[0]);
nbr = getint(argv[2], 0, MAXSTRLEN);
if(nbr > *s) nbr = *s; // get the number of chars to copy
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
p1 = sret; p2 = s + (*s - nbr) + 1;
*p1++ = nbr; // inset the length of the returned string
while(nbr--) *p1++ = *p2++; // and copy the characters
targ = T_STR;
}
// return the length of a string
// nbr = LEN( string$ )
void fun_len(void) {
iret = *(unsigned char *)getstring(ep); // first byte is the length
targ = T_INT;
}
// Return the natural logarithm of the argument 'number'.
// n = LOG( number )
void fun_log(void) {
MMFLOAT f;
f = getnumber(ep);
if(f == 0) error("Divide by zero");
if(f < 0) error("Negative argument");
fret = log(f);
targ = T_NBR;
}
// Returns a substring of ?string$? beginning at ?start? and continuing for ?nbr? characters.
// S$ = MID$(s, spos [, nbr])
void fun_mid(void) {
unsigned char *s, *p1, *p2;
int spos, nbr = 0, i;
getargs(&ep, 5, (unsigned char *)",");
if(argc == 5) { // we have MID$(s, n, m)
nbr = getint(argv[4], 0, MAXSTRLEN); // nbr of chars to return
}
else if(argc == 3) { // we have MID$(s, n)
nbr = MAXSTRLEN; // default to all chars
}
else
error("Argument count");
s = getstring(argv[0]); // the string
spos = getint(argv[2], 1, MAXSTRLEN); // the mid position
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
targ = T_STR;
if(spos > *s || nbr == 0) // if the numeric args are not in the string
return; // return a null string
else {
i = *s - spos + 1; // find how many chars remaining in the string
if(i > nbr) i = nbr; // reduce it if we don't need that many
p1 = sret; p2 = s + spos;
*p1++ = i; // set the length of the MMBasic string
while(i--) *p1++ = *p2++; // copy the nbr chars required
}
}
// Return the value of Pi. Thanks to Alan Williams for the contribution
// n = PI
void fun_pi(void) {
fret = M_PI;
targ = T_NBR;
}
// convert degrees to radians. Thanks to Alan Williams for the contribution
// r = RAD( degrees )
void fun_rad(void) {
fret = (MMFLOAT)((MMFLOAT)getnumber(ep)/RADCONV);
targ = T_NBR;
}
// generate a random number that is greater than or equal to 0 but less than 1
// n = RND()
void fun_rnd(void) {
#ifdef rp2350
fret = (MMFLOAT)get_rand_32()/(MMFLOAT)0x100000000;
#else
fret = (MMFLOAT)rand()/((MMFLOAT)RAND_MAX + (MMFLOAT)RAND_MAX/1000000);
#endif
targ = T_NBR;
}
// Return the sign of the argument
// n = SGN( number )
void fun_sgn(void) {
MMFLOAT f;
f = getnumber(ep);
if(f > 0)
iret = +1;
else if(f < 0)
iret = -1;
else
iret = 0;
targ = T_INT;
}
// Return the sine of the argument 'number' in radians.
// n = SIN( number )
void fun_sin(void) {
if(useoptionangle){
#ifndef rp2350
MMFLOAT t=getnumber(ep);
if (t == (int)t) {
int integerPart = (int)t;
// Modulus 360 and ensure it's in the range [0, 359]
fret= sinetab[(integerPart % 360 + 360) % 360];
}
else
#endif
fret=sin(getnumber(ep)/optionangle);
} else {
fret = sin(getnumber(ep));
}
targ = T_NBR;
}
// Return the square root of the argument 'number'.
// n = SQR( number )
void fun_sqr(void) {
MMFLOAT f;
f = getnumber(ep);
if(f < 0) error("Negative argument");
fret = sqrt(f);
targ = T_NBR;
}
void fun_tan(void) {
if(useoptionangle){
#ifndef rp2350
MMFLOAT t=getnumber(ep);
if (t == (int)t) {
int integerPart = (int)t;
// Modulus 360 and ensure it's in the range [0, 359]
MMFLOAT cosval=sinetab[(integerPart % 360 + 450) % 360];
if(cosval==0.0)error("Overflow");
fret= sinetab[(integerPart % 360 + 360) % 360]/cosval;
}
else
#endif
fret=tan(getnumber(ep)/optionangle);
} else {
fret = tan(getnumber(ep));
}
targ = T_NBR;
}
// Returns the numerical value of the ?string$?.
// n = VAL( string$ )
void fun_val(void) {
unsigned char *p, *t1, *t2;
p = getCstring(ep);
targ = T_INT;
if(*p == '&') {
p++; iret = 0;
switch(toupper(*p++)) {
case 'H': while(isxdigit(*p)) {
iret = (iret << 4) | ((toupper(*p) >= 'A') ? toupper(*p) - 'A' + 10 : *p - '0');
p++;
}
break;
case 'O': while(*p >= '0' && *p <= '7') {
iret = (iret << 3) | (*p++ - '0');
}
break;
case 'B': while(*p == '0' || *p == '1') {
iret = (iret << 1) | (*p++ - '0');
}
break;
default : iret = 0;
}
} else {
fret = (MMFLOAT) strtod((char *)p, (char **)&t1);
iret = strtoll((char *)p, (char **)&t2, 10);
if (t1 > t2) targ = T_NBR;
}
}
void fun_eval(void) {
unsigned char *s, *st, *temp_tknbuf;
int t;
temp_tknbuf = GetTempMemory(STRINGSIZE);
strcpy((char *)temp_tknbuf, (char *)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
st = GetTempMemory(STRINGSIZE);
strcpy((char *)st, (char *)getstring(ep)); // then copy the argument
MtoC(st); // and convert to a C string
inpbuf[0] = 'r'; inpbuf[1] = '='; // place a dummy assignment in the input buffer to keep the tokeniser happy
strcpy((char *)inpbuf + 2, (char *)st);
multi=false;
tokenise(true); // and tokenise it (the result is in tknbuf)
strcpy((char *)st, (char *)(tknbuf + 2 + sizeof(CommandToken)));
t = T_NOTYPE;
evaluate(st, &fret, &iret, &s, &t, false); // get the value and type of the argument
if(t & T_STR) {
Mstrcpy(st, s); // if it is a string then save it
sret = st;
}
targ=t;
strcpy((char *)tknbuf, (char *)temp_tknbuf); // restore the saved token buffer
}
void fun_errno(void) {
iret = MMerrno;
targ = T_INT;
}
void fun_errmsg(void) {
sret = GetTempMemory(STRINGSIZE);
strcpy((char *)sret, MMErrMsg);
CtoM(sret);
targ = T_STR;
}
// Returns a string of blank spaces 'number' bytes long.
// s$ = SPACE$( number )
void fun_space(void) {
int i;
i = getint(ep, 0, MAXSTRLEN);
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
memset(sret + 1, ' ', i);
*sret = i;
targ = T_STR;
}
// Returns a string in the decimal (base 10) representation of 'number'.
// s$ = STR$( number, m, n, c$ )
void fun_str(void) {
unsigned char *s;
MMFLOAT f;
long long int i64;
int t;
int m, n;
unsigned char ch, *p;
getargs(&ep, 7, (unsigned char *)",");
if((argc & 1) != 1) error("Syntax");
t = T_NOTYPE;
p = evaluate(argv[0], &f, &i64, &s, &t, false); // get the value and type of the argument
if(!(t & T_INT || t & T_NBR)) error("Expected a number");
m = 0; n = STR_AUTO_PRECISION; ch = ' ';
if(argc > 2) m = getint(argv[2], -128, 128); // get the number of digits before the point
if(argc > 4) n = getint(argv[4], -20, 20); // get the number of digits after the point
if(argc == 7) {
p = getstring(argv[6]);
if(*p == 0) error("Zero length argument");
ch = ((unsigned char)p[1] & 0x7f);
}
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
if(t & T_NBR)
FloatToStr((char *)sret, f, m, n, ch); // convert the float
else {
if(n < 0)
FloatToStr((char *)sret, i64, m, n, ch); // convert as a float
else {
IntToStrPad((char *)sret, i64, ch, m, 10); // convert the integer
if(n != STR_AUTO_PRECISION && n > 0) {
strcat((char *)sret, ".");
while(n--) strcat((char *)sret, "0"); // and add on any zeros after the point
}
}
}
CtoM(sret);
targ = T_STR;
}
// Returns a string 'nbr' bytes long
// s$ = STRING$( nbr, string$ )
void fun_string(void) {
int i, j, t = T_NOTYPE;
void *p;
getargs(&ep, 3, (unsigned char *)",");
if(argc != 3) error("Syntax");
i = getint(argv[0], 0, MAXSTRLEN);
p = DoExpression(argv[2], &t); // get the value and type of the argument
if(t & T_STR) {
if(!*(char *)p) error("Argument value: $", argv[2]);
j = *((char *)p + 1);
} else if(t & T_INT)
j = *(long long int *)p;
else
j = FloatToInt32(*((MMFLOAT *)p));
if(j < 0 || j > 255) error("Argument value: $", argv[2]);
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
memset(sret + 1, j, i);
*sret = i;
targ = T_STR;
}
// Returns string$ converted to uppercase characters.
// s$ = UCASE$( string$ )
void fun_ucase(void) {
unsigned char *s, *p;
int i;
s = getstring(ep);
p = sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
i = *p++ = *s++; // get the length of the string and save in the destination
while(i--) {
*p = toupper(*s);
p++; s++;
}
targ = T_STR;
}
// Returns string$ converted to lowercase characters.
// s$ = LCASE$( string$ )
void fun_lcase(void) {
unsigned char *s, *p;
int i;
s = getstring(ep);
p = sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
i = *p++ = *s++; // get the length of the string and save in the destination
while(i--) {
*p = tolower(*s);
p++; s++;
}
targ = T_STR;
}
// function (which looks like a pre defined variable) to return the version number
// it pulls apart the VERSION string to generate the number
void fun_version(void){
char *p;
fret = strtol(VERSION, &p, 10);
fret += (MMFLOAT)strtol(p + 1, &p, 10) / 100;
fret += (MMFLOAT)strtol(p + 1, &p, 10) / 10000;
fret += (MMFLOAT)strtol(p + 1, &p, 10) / 1000000;
targ = T_NBR;
}
// Returns the current cursor position in the line in characters.
// n = POS
void fun_pos(void){
iret = MMCharPos;
targ = T_INT;
}
// Outputs spaces until the column indicated by 'number' has been reached.
// PRINT TAB( number )
void fun_tab(void) {
int i;
unsigned char *p;
i = getint(ep, 1, 255);
sret = p = GetTempMemory(STRINGSIZE); // this will last for the life of the command
if(MMCharPos > i) {
i--;
*p++ = '\r';
*p++ = '\n';
}
else
i -= MMCharPos;
memset(p, ' ', i);
p[i] = 0;
CtoM(sret);
targ = T_STR;
}
// get a character from the console input queue
// s$ = INKEY$
void __not_in_flash_func(fun_inkey)(void){
int i;
sret = GetTempMemory(STRINGSIZE); // this buffer is automatically zeroed so the string is zero size
i = MMInkey();
if(i != -1) {
sret[0] = 1; // this is the length
sret[1] = i; // and this is the character
}
targ = T_STR;
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
// used by ACos() and ASin() below
MMFLOAT arcsinus(MMFLOAT x) {
return 2.0L * atan(x / (1.0L + sqrt(1.0L - x * x)));
}
/* @endcond */
// Return the arcsine (in radians) of the argument 'number'.
// n = ASIN(number)
void fun_asin(void) {
MMFLOAT f = getnumber(ep);
if(f < -1.0 || f > 1.0) error("Number out of bounds");
if (f == 1.0) {
fret = M_PI_2;
} else if (f == -1.0) {
fret = -M_PI_2;
} else {
fret = arcsinus(f);
}
if(useoptionangle)fret *=optionangle;
targ = T_NBR;
}
// Return the arccosine (in radians) of the argument 'number'.
// n = ACOS(number)
void fun_acos(void) {
MMFLOAT f = getnumber(ep);
if(f < -1.0L || f > 1.0L) error("Number out of bounds");
if (f == 1.0L) {
fret = 0.0L;
} else if (f == -1.0L) {
fret = M_PI;
} else {
fret = M_PI_2 - arcsinus(f);
}
if(useoptionangle)fret *=optionangle;
targ = T_NBR;
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
// utility function to do the max/min comparison and return the value
// it is only called by fun_max() and fun_min() below.
void do_max_min(int cmp) {
int i;
MMFLOAT nbr, f;
getargs(&ep, (MAX_ARG_COUNT * 2) - 1, (unsigned char *)",");
if((argc & 1) != 1) error("Syntax");
if(cmp) nbr = -FLT_MAX; else nbr = FLT_MAX;
for(i = 0; i < argc; i += 2) {
f = getnumber(argv[i]);
if(cmp && f > nbr) nbr = f;
if(!cmp && f < nbr) nbr = f;
}
fret = nbr;
targ = T_NBR;
}
/* @endcond */
void fun_max(void) {
do_max_min(1);
}
void fun_min(void) {
do_max_min(0);
}
#ifdef rp2350
void __not_in_flash_func(fun_ternary)(void){
#else
#ifdef PICOMITEVGA
void fun_ternary(void){
#else
void __not_in_flash_func(fun_ternary)(void){
#endif
#endif
MMFLOAT f = 0;
long long int i64 = 0;
unsigned char *s = NULL;
int t = T_NOTYPE;
getargs(&ep,5,(unsigned char *)",");
if(argc!=5)error("Syntax");
int which=getnumber(argv[0]);
if(which){
evaluate(argv[2], &f, &i64, &s, &t, false);
} else {
evaluate(argv[4], &f, &i64, &s, &t, false);
}
if(t & T_INT){
iret=i64;
targ=T_INT;
return;
} else if (t & T_NBR){
fret=f;
targ=T_NBR;
return;
} else if (t & T_STR){
sret=GetTempMemory(STRINGSIZE);
Mstrcpy(sret, s); // copy the string
targ=T_STR;
return;
} else error("Syntax");
}