2025-05-19 14:50:19 +08:00

1180 lines
48 KiB
C

/***********************************************************************************************************************
PicoMite MMBasic
Memory.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 Memory.c
* @author Geoff Graham, Peter Mather
* @brief Source for the MMBasic Memory command
*/
/**
* @cond
* The following section will be excluded from the documentation.
*/
#define INCLUDE_FUNCTION_DEFINES
#include <stdio.h>
#include "MMBasic_Includes.h"
#include "Hardware_Includes.h"
#define ASMMAX 6400 // maximum number of bytes that can be copied or set by assembler routines
#define MAXCPY 3200 // tuned maximum number of bytes to copy using ZCOPY
extern const uint8_t *SavedVarsFlash;
extern const uint8_t *flash_progmemory;
// memory management parameters
// allocate static memory for programs, variables and the heap
// this is simple memory management because DOS has plenty of memory
//unsigned char __attribute__ ((aligned (256))) AllMemory[ALL_MEMORY_SIZE];
#ifdef rp2350
#ifdef PICOMITEVGA
unsigned char __attribute__ ((aligned (4096))) AllMemory[HEAP_MEMORY_SIZE+256+320*240*2];
unsigned char *FRAMEBUFFER=AllMemory+HEAP_MEMORY_SIZE+256;
uint32_t framebuffersize=320*240*2;
#else
unsigned char __attribute__ ((aligned (256))) AllMemory[HEAP_MEMORY_SIZE+256];
#endif
unsigned char *MMHeap=AllMemory;
#else
#ifdef PICOMITEVGA
unsigned char __attribute__ ((aligned (4096))) Heap[HEAP_MEMORY_SIZE+256];
unsigned char __attribute__ ((aligned (256))) video[640*480/8];
unsigned char *FRAMEBUFFER=video;
uint32_t framebuffersize=640*480/8;
unsigned char *MMHeap=Heap;
#else
unsigned char __attribute__ ((aligned (256))) AllMemory[HEAP_MEMORY_SIZE+256];
unsigned char *MMHeap=AllMemory;
#endif
#endif
uint32_t heap_memory_size=HEAP_MEMORY_SIZE;
#ifdef PICOMITEVGA
#ifdef rp2350
uint16_t *tilefcols;//=(uint16_t *)((uint32_t)FRAMEBUFFER+(MODE1SIZE_S*3));
uint16_t *tilebcols;//=(uint16_t *)((uint32_t)FRAMEBUFFER+(MODE1SIZE_S*3)+(MODE1SIZE_S>>1));
#else
uint16_t __attribute__ ((aligned (256))) tilefcols[80*40];
uint16_t __attribute__ ((aligned (256))) tilebcols[80*40];
#endif
#ifdef HDMI
uint8_t *tilefcols_w;
uint8_t *tilebcols_w;
uint16_t HDMIlines[2][848]={0};
volatile int X_TILE=80, Y_TILE=40;
uint32_t core1stack[128];
volatile int ytileheight=480/12;
#else
uint16_t M_Foreground[16] ={
0x0000,0x000F,0x00f0,0x00ff,0x0f00,0x0f0F,0x0ff0,0x0fff,0xf000,0xf00F,0xf0f0,0xf0ff,0xff00,0xff0F,0xfff0,0xffff
};
uint16_t M_Background[16] ={
0xffff,0xfff0,0xff0f,0xff00,0xf0ff,0xf0f0,0xf00f,0xf000,0x0fff,0x0ff0,0x0f0f,0x0f00,0x00ff,0x00f0,0x000f,0x0000
};
volatile int ytileheight=16;
#endif
#ifdef rp2350
unsigned char *WriteBuf=AllMemory+HEAP_MEMORY_SIZE+256;
unsigned char *DisplayBuf=AllMemory+HEAP_MEMORY_SIZE+256;
unsigned char *LayerBuf=AllMemory+HEAP_MEMORY_SIZE+256;
unsigned char *FrameBuf=AllMemory+HEAP_MEMORY_SIZE+256;
unsigned char *SecondLayer=AllMemory+HEAP_MEMORY_SIZE+256;
unsigned char *SecondFrame=AllMemory+HEAP_MEMORY_SIZE+256;
#else
unsigned char *WriteBuf=video;
unsigned char *DisplayBuf=video;
unsigned char *LayerBuf=video;
unsigned char *FrameBuf=video;
unsigned char *SecondLayer=video;
unsigned char *SecondFrame=video;
#endif
#endif
#ifdef PICOMITE
unsigned char *WriteBuf=NULL;
unsigned char *LayerBuf=NULL;
unsigned char *FrameBuf=NULL;
#endif
#ifdef GUICONTROLS
struct s_ctrl CTRLS[MAXCONTROLS];
struct s_ctrl *Ctrl=NULL;
#endif
#ifdef PICOMITEWEB
unsigned char *WriteBuf=NULL;
unsigned char *LayerBuf=NULL;
unsigned char *FrameBuf=NULL;
#endif
unsigned int mmap[HEAP_MEMORY_SIZE/ PAGESIZE / PAGESPERWORD]={0};
#ifdef rp2350
unsigned int psmap[6*1024*1024/ PAGESIZE / PAGESPERWORD]={0};
unsigned int SBitsGet(unsigned char *addr);
void SBitsSet(unsigned char *addr, int bits);
#endif
static inline unsigned int MBitsGet(unsigned char *addr);
static inline void MBitsSet(unsigned char *addr, int bits);
volatile char *g_StrTmp[MAXTEMPSTRINGS]; // used to track temporary string space on the heap
volatile char g_StrTmpLocalIndex[MAXTEMPSTRINGS]; // used to track the g_LocalIndex for each temporary string space on the heap
void *getheap(int size);
unsigned int UsedHeap(void);
bool g_TempMemoryIsChanged = false; // used to prevent unnecessary scanning of strtmp[]
short g_StrTmpIndex = 0; // index to the next unallocated slot in strtmp[]
/***********************************************************************************************************************
MMBasic commands
************************************************************************************************************************/
/* @endcond */
void MIPS16 cmd_memory(void) {
unsigned char *p,*tp;
tp = checkstring(cmdline, (unsigned char *)"PACK");
if(tp){
getargs(&tp,7,(unsigned char *)",");
if(argc!=7)error("Syntax");
int i,n=getinteger(argv[4]);
if(n<=0)return;
int size=getint(argv[6],1,32);
if(!(size==1 || size==4 || size==8 || size==16 || size==32))error((char *)"Invalid size");
int sourcesize,destinationsize;
void *top=NULL;
uint64_t *from=NULL;
if(CheckEmpty((char *)argv[0])){
sourcesize=parseintegerarray(argv[0],(int64_t **)&from, 1,1,NULL,false);
if(sourcesize<n)error("Source array too small");
} else from=(uint64_t *)GetPokeAddr(argv[0]);
if(CheckEmpty((char *)argv[2])){
destinationsize=parseintegerarray(argv[2],(int64_t **)&top, 2,1,NULL,true);
if(destinationsize*64/size<n)error("Destination array too small");
} else top=(void *)GetPokeAddr(argv[2]);
if((uint32_t)from % 8)error("Source address not divisible by 8");
if(size==1){
uint8_t *to=(uint8_t *)top;
for(i=0;i<n;i++){
int s= i % 8;
if(s==0)*to=0;
*to |= ((*from++) & 0x1)<<s;
if(s==7)to++;
}
} else if(size==4){
uint8_t *to=(uint8_t *)top;
for(i=0;i<n;i++){
if((i & 1) == 0){
*to=(*from++) & 0xF;
} else {
*to |= ((*from++) & 0xF)<<4;
to++;
}
}
} else if(size==8){
uint8_t *to=(uint8_t *)top;
while(n--){
*to++=(uint8_t)*from++;
}
} else if(size==16){
uint16_t *to=(uint16_t *)top;
if((uint32_t)to % 2)error("Destination address not divisible by 2");
while(n--){
*to++=(uint16_t)*from++;
}
} else if(size==32){
uint32_t *to=(uint32_t *)top;
if((uint32_t)to % 4)error("Destination address not divisible by 4");
while(n--){
*to++=(uint32_t)*from++;
}
}
return;
}
tp = checkstring(cmdline, (unsigned char *)"PRINT");
if(tp){
char *fromp=NULL;
int sourcesize;
int64_t *aint;
getargs(&tp,5,(unsigned char *)",");
if(!(argc==5))error("Syntax");
if(*argv[0] == '#') argv[0]++;
int fnbr = getint(argv[0],1,MAXOPENFILES); // get the number
int n=getinteger(argv[2]);
if(CheckEmpty((char *)argv[4])){
sourcesize=parseintegerarray(argv[4],&aint,3,1,NULL,false);
if(sourcesize*8<n)error("Source array too small");
fromp=(char *)aint;
} else {
fromp=(char *)GetPeekAddr(argv[4]);
}
if (FileTable[fnbr].com > MAXCOMPORTS)
{
FilePutStr(n, fromp, fnbr);
}
else error("File % not open",fnbr);
return;
}
tp = checkstring(cmdline, (unsigned char *)"INPUT");
if(tp){
char *fromp=NULL;
int sourcesize;
int64_t *aint;
getargs(&tp,5,(unsigned char *)",");
if(!(argc==5))error("Syntax");
if(*argv[0] == '#') argv[0]++;
int fnbr = getint(argv[0],1,MAXOPENFILES); // get the number
int n=getinteger(argv[2]);
if(CheckEmpty((char *)argv[4])){
sourcesize=parseintegerarray(argv[4],&aint,3,1,NULL,false);
if(sourcesize*8<n)error("Source array too small");
fromp=(char *)aint;
} else {
fromp=(char *)GetPokeAddr(argv[4]);
}
if (FileTable[fnbr].com > MAXCOMPORTS)
{
while(!(MMfeof(fnbr)) && n--) *fromp++=FileGetChar(fnbr);
if(n)error("End of file");
}
else error("File % not open",fnbr);
return;
}
tp = checkstring(cmdline, (unsigned char *)"UNPACK");
if(tp){
getargs(&tp,7,(unsigned char *)",");
if(argc!=7)error("Syntax");
int i,n=getinteger(argv[4]);
if(n<=0)return;
int size=getint(argv[6],1,32);
if(!(size==1 || size==4 || size==8 || size==16 || size==32))error((char *)"Invalid size");
int sourcesize,destinationsize;
uint64_t *to=NULL;
void *fromp=NULL;
if(CheckEmpty((char *)argv[0])){
sourcesize=parseintegerarray(argv[0],(int64_t **)&fromp, 1,1,NULL,false);
if(sourcesize*64/size<n)error("Source array too small");
} else {
fromp=(void*)GetPokeAddr(argv[0]);
}
if(CheckEmpty((char *)argv[2])){
destinationsize=parseintegerarray(argv[2],(int64_t **)&to, 2,1,NULL,true);
if(n>destinationsize)error("Destination array too small");
} else to=(uint64_t *)GetPokeAddr(argv[2]);
if((uint32_t)to % 8)error("Source address not divisible by 8");
if(size==1){
uint8_t *from=(uint8_t *)fromp;
for(i=0;i<n;i++){
int s= i % 8;
*to++ = ((*from & (1<<s)) ? 1 : 0);
if(s==7)from++;
}
} else if(size==4){
uint8_t *from=(uint8_t *)fromp;
for(i=0;i<n;i++){
if((i & 1) == 0){
*to++=(*from) & 0xF;
} else {
*to++ = (*from) >> 4;
from++;
}
}
} else if(size==8){
uint8_t *from=(uint8_t *)fromp;
while(n--){
*to++=(uint64_t)*from++;
}
} else if(size==16){
uint16_t *from=(uint16_t *)fromp;
if((uint32_t)from % 2)error("Source address not divisible by 2");
while(n--){
*to++=(uint64_t)*from++;
}
} else if(size==32){
uint32_t *from=(uint32_t *)fromp;
if((uint32_t)from % 4)error("Source address not divisible by 4");
while(n--){
*to++=(uint64_t)*from++;
}
}
return;
}
tp = checkstring(cmdline, (unsigned char *)"COPY");
if(tp){
if((p = checkstring(tp, (unsigned char *)"INTEGER"))) {
int stepin=1, stepout=1;
getargs(&p,9,(unsigned char *)",");
if(argc<5)error("Syntax");
int n=getinteger(argv[4]);
if(n<=0)return;
uint64_t *from=(uint64_t *)GetPokeAddr(argv[0]);
uint64_t *to=(uint64_t *)GetPokeAddr(argv[2]);
if((uint32_t)from % 8)error("Address not divisible by 8");
if((uint32_t)to % 8)error("Address not divisible by 8");
if(argc>=7 && *argv[6])stepin=getint(argv[6],0,0xFFFF);
if(argc==9)stepout=getint(argv[8],0,0xFFFF);
if(stepin==1 && stepout==1)memmove(to, from, n*8);
else{
if(from<to){
from+=(n-1)*stepin;
to+=(n-1)*stepout;
while(n--){
*to=*from;
to-=stepout;
from-=stepin;
}
} else {
while(n--){
*to=*from;
to+=stepout;
from+=stepin;
}
}
}
return;
}
if((p = checkstring(tp, (unsigned char *)"FLOAT"))) {
int stepin=1, stepout=1;
getargs(&p,9,(unsigned char *)","); //assume byte
if(argc<5)error("Syntax");
int n=getinteger(argv[4]);
if(n<=0)return;
MMFLOAT *from=(MMFLOAT *)GetPokeAddr(argv[0]);
MMFLOAT *to=(MMFLOAT *)GetPokeAddr(argv[2]);
if((uint32_t)from % 8)error("Address not divisible by 8");
if((uint32_t)to % 8)error("Address not divisible by 8");
if(argc>=7 && *argv[6])stepin=getint(argv[6],0,0xFFFF);
if(argc==9)stepout=getint(argv[8],0,0xFFFF);
if(n<=0)return;
if(stepin==1 && stepout==1)memmove(to, from, n*8);
else{
if(from<to){
from+=(n-1)*stepin;
to+=(n-1)*stepout;
while(n--){
*to=*from;
to-=stepout;
from-=stepin;
}
} else {
while(n--){
*to=*from;
to+=stepout;
from+=stepin;
}
}
}
return;
}
getargs(&tp,9,(unsigned char *)","); //assume byte
if(argc<5)error("Syntax");
int stepin=1, stepout=1;
char *from=(char *)GetPeekAddr(argv[0]);
char *to=(char *)GetPokeAddr(argv[2]);
int n=getinteger(argv[4]);
if(argc>=7 && *argv[6])stepin=getint(argv[6],0,0xFFFF);
if(argc==9)stepout=getint(argv[8],0,0xFFFF);
if(n<=0)return;
if(stepin==1 && stepout==1)memmove(to, from, n);
else {
if(from<to){
from+=(n-1)*stepin;
to+=(n-1)*stepout;
while(n--){
*to=*from;
to-=stepout;
from-=stepin;
}
} else {
while(n--){
*to=*from;
to+=stepout;
from+=stepin;
}
}
}
return;
}
tp = checkstring(cmdline, (unsigned char *)"SET");
if(tp){
unsigned char *p;
if((p = checkstring(tp, (unsigned char *)"BYTE"))) {
getargs(&p,5,(unsigned char *)","); //assume byte
if(argc!=5)error("Syntax");
char *to=(char *)GetPokeAddr(argv[0]);
int val=getint(argv[2],0,255);
int n=getinteger(argv[4]);
if(n<=0)return;
memset(to, val, n);
return;
}
if((p = checkstring(tp, (unsigned char *)"SHORT"))) {
getargs(&p,5,(unsigned char *)","); //assume byte
if(argc!=5)error("Syntax");
short *to=(short *)GetPokeAddr(argv[0]);
if((uint32_t)to % 2)error("Address not divisible by 2");
short *q=to;
short data=getint(argv[2],0,65535);
int n=getinteger(argv[4]);
if(n<=0)return;
while(n>0){
*q++=data;
n--;
}
return;
}
if((p = checkstring(tp, (unsigned char *)"WORD"))) {
getargs(&p,5,(unsigned char *)","); //assume byte
if(argc!=5)error("Syntax");
unsigned int *to=(unsigned int *)GetPokeAddr(argv[0]);
if((uint32_t)to % 4)error("Address not divisible by 4");
unsigned int *q=to;
unsigned int data=getint(argv[2],0,0xFFFFFFFF);
int n=getinteger(argv[4]);
if(n<=0)return;
while(n>0){
*q++=data;
n--;
}
return;
}
if((p = checkstring(tp, (unsigned char *)"INTEGER"))) {
int stepin=1;
getargs(&p,7,(unsigned char *)",");
if(argc<5)error("Syntax");
uint64_t *to=(uint64_t *)GetPokeAddr(argv[0]);
if((uint32_t)to % 8)error("Address not divisible by 8");
int64_t data;
data=getinteger(argv[2]);
int n=getinteger(argv[4]);
if(argc==7)stepin=getint(argv[6],0,0xFFFF);
if(n<=0)return;
if(stepin==1)while(n--)*to++=data;
else{
while(n--){
*to=data;
to+=stepin;
}
}
return;
}
if((p = checkstring(tp, (unsigned char *)"FLOAT"))) {
int stepin=1;
getargs(&p,7,(unsigned char *)","); //assume byte
if(argc<5)error("Syntax");
MMFLOAT *to=(MMFLOAT *)GetPokeAddr(argv[0]);
if((uint32_t)to % 8)error("Address not divisible by 8");
MMFLOAT data;
data=getnumber(argv[2]);
int n=getinteger(argv[4]);
if(argc==7)stepin=getint(argv[6],0,0xFFFF);
if(n<=0)return;
if(stepin==1)while(n--)*to++=data;
else{
while(n--){
*to=data;
to+=stepin;
}
}
return;
}
getargs(&tp,5,(unsigned char *)","); //assume byte
if(argc!=5)error("Syntax");
char *to=(char *)GetPokeAddr(argv[0]);
int val=getint(argv[2],0,255);
int n=getinteger(argv[4]);
if(n<=0)return;
memset(to, val, n);
return;
}
//MEMORY Usage
int i, j, var, nbr, vsize, VarCnt;
int ProgramSize, ProgramPercent, VarSize, VarPercent, GeneralSize, GeneralPercent, SavedVarSize, SavedVarSizeK, SavedVarPercent, SavedVarCnt;
int CFunctSize, CFunctSizeK, CFunctNbr, CFunctPercent, FontSize, FontSizeK, FontNbr, FontPercent, LibrarySizeK, LibraryPercent,LibraryMaxK;
unsigned int CurrentRAM, *pint;
CurrentRAM = heap_memory_size + MAXVARS * sizeof(struct s_vartbl);
#ifdef rp2350
CurrentRAM+=PSRAMsize;
#endif
// calculate the space allocated to variables on the heap
for(i = VarCnt = vsize = var = 0; var < MAXVARS; var++) {
if(g_vartbl[var].type == T_NOTYPE) continue;
VarCnt++; vsize += sizeof(struct s_vartbl);
if(g_vartbl[var].val.s == NULL) continue;
if(g_vartbl[var].type & T_PTR) continue;
nbr = g_vartbl[var].dims[0] + 1 - g_OptionBase;
if(g_vartbl[var].dims[0]) {
for(j = 1; j < MAXDIM && g_vartbl[var].dims[j]; j++)
nbr *= (g_vartbl[var].dims[j] + 1 - g_OptionBase);
if(g_vartbl[var].type & T_NBR)
i += MRoundUp(nbr * sizeof(MMFLOAT));
else if(g_vartbl[var].type & T_INT)
i += MRoundUp(nbr * sizeof(long long int));
else
i += MRoundUp(nbr * (g_vartbl[var].size + 1));
} else
if(g_vartbl[var].type & T_STR)
i += STRINGSIZE;
}
VarSize = (vsize + i + 512)/1024; // this is the memory allocated to variables
VarPercent = ((vsize + i) * 100)/CurrentRAM;
if(VarCnt && VarSize == 0) VarPercent = VarSize = 1; // adjust if it is zero and we have some variables
i = UsedHeap() - i;
if(i < 0) i = 0;
GeneralSize = (i + 512)/1024; GeneralPercent = (i * 100)/CurrentRAM;
// count the space used by saved variables (in flash)
p = (unsigned char *)SavedVarsFlash;
SavedVarCnt = 0;
while(!(*p == 0 || *p == 0xff)) {
unsigned char type, array;
SavedVarCnt++;
type = *p++;
array = type & 0x80; type &= 0x7f; // set array to true if it is an array
p += strlen((char *)p) + 1;
if(array)
p += (p[0] | p[1] << 8 | p[2] << 16| p[3] << 24) + 4;
else {
if(type & T_NBR)
p += sizeof(MMFLOAT);
else if(type & T_INT)
p += sizeof(long long int);
else
p += *p + 1;
}
}
SavedVarSize = p - (SavedVarsFlash);
SavedVarSizeK = (SavedVarSize + 512) / 1024;
SavedVarPercent = (SavedVarSize * 100) / (/*MAX_PROG_SIZE +*/ SAVEDVARS_FLASH_SIZE);
if(SavedVarCnt && SavedVarSizeK == 0) SavedVarPercent = SavedVarSizeK = 1; // adjust if it is zero and we have some variables
// count the space used by CFunctions, CSubs and fonts
CFunctSize = CFunctNbr = FontSize = FontNbr = 0;
pint = (unsigned int *)CFunctionFlash;
while(*pint != 0xffffffff) {
//if(*pint < FONT_TABLE_SIZE) {
if(*pint >> 31 ){
pint++;
FontNbr++;
FontSize += *pint + 8;
} else {
pint++;
CFunctNbr++;
CFunctSize += *pint + 8;
}
pint += (*pint + 4) / sizeof(unsigned int);
}
CFunctPercent = (CFunctSize * 100) / (MAX_PROG_SIZE + SAVEDVARS_FLASH_SIZE);
CFunctSizeK = (CFunctSize + 512) / 1024;
if(CFunctNbr && CFunctSizeK == 0) CFunctPercent = CFunctSizeK = 1; // adjust if it is zero and we have some functions
FontPercent = (FontSize * 100) / (MAX_PROG_SIZE /*+ SAVEDVARS_FLASH_SIZE*/);
FontSizeK = (FontSize + 512) / 1024;
if(FontNbr && FontSizeK == 0) FontPercent = FontSizeK = 1; // adjust if it is zero and we have some functions
// count the number of lines in the program
p = ProgMemory;
i = 0;
while(*p != 0xff) { // skip if program memory is erased
if(*p == 0) p++; // if it is at the end of an element skip the zero marker
if(*p == 0) break; // end of the program or module
if(*p == T_NEWLINE) {
i++; // count the line
p++; // skip over the newline token
}
if(*p == T_LINENBR) p += 3; // skip over the line number
skipspace(p);
if(p[0] == T_LABEL) p += p[1] + 2; // skip over the label
while(*p) p++; // look for the zero marking the start of an element
}
ProgramSize = ((p - ProgMemory) + 512)/1024;
ProgramPercent = ((p - ProgMemory) * 100)/(MAX_PROG_SIZE /*+ SAVEDVARS_FLASH_SIZE*/);
if(ProgramPercent > 100) ProgramPercent = 100;
if(i && ProgramSize == 0) ProgramPercent = ProgramSize = 1; // adjust if it is zero and we have some lines
MMPrintString("Program:\r\n");
IntToStrPad((char *)inpbuf, ProgramSize, ' ', 4, 10); strcat((char *)inpbuf, "K (");
IntToStrPad((char *)inpbuf + strlen((char *)inpbuf), ProgramPercent, ' ', 2, 10); strcat((char *)inpbuf, "%) Program (");
IntToStr((char *)inpbuf + strlen((char *)inpbuf), i, 10); strcat((char *)inpbuf, " lines)\r\n");
MMPrintString((char *)inpbuf);
if(CFunctNbr) {
IntToStrPad((char *)inpbuf, CFunctSizeK, ' ', 4, 10); strcat((char *)inpbuf, "K (");
IntToStrPad((char *)inpbuf + strlen((char *)inpbuf), CFunctPercent, ' ', 2, 10); strcat((char *)inpbuf, "%) "); MMPrintString((char *)inpbuf);
IntToStr((char *)inpbuf, CFunctNbr, 10); strcat((char *)inpbuf, " Embedded C Routine"); strcat((char *)inpbuf, CFunctNbr == 1 ? "\r\n":"s\r\n");
MMPrintString((char *)inpbuf);
}
if(FontNbr) {
IntToStrPad((char *)inpbuf, FontSizeK, ' ', 4, 10); strcat((char *)inpbuf, "K (");
IntToStrPad((char *)inpbuf + strlen((char *)inpbuf), FontPercent, ' ', 2, 10); strcat((char *)inpbuf, "%) "); MMPrintString((char *)inpbuf);
IntToStr((char *)inpbuf, FontNbr, 10); strcat((char *)inpbuf, " Embedded Fonts"); strcat((char *)inpbuf, FontNbr == 1 ? "\r\n":"s\r\n");
MMPrintString((char *)inpbuf);
}
/*
if(SavedVarCnt) {
IntToStrPad(inpbuf, SavedVarSizeK, ' ', 4, 10); strcat((char *)inpbuf, "K (");
IntToStrPad(inpbuf + strlen(inpbuf), SavedVarPercent, ' ', 2, 10); strcat((char *)inpbuf, "%)");
IntToStrPad(inpbuf + strlen(inpbuf), SavedVarCnt, ' ', 2, 10); strcat((char *)inpbuf, " Saved Variable"); strcat((char *)inpbuf, SavedVarCnt == 1 ? " (":"s (");
IntToStr((char *)inpbuf + strlen(inpbuf), SavedVarSize, 10); strcat((char *)inpbuf, " bytes)\r\n");
MMPrintString(inpbuf);
}
*/
IntToStrPad((char *)inpbuf, ((MAX_PROG_SIZE/* + SAVEDVARS_FLASH_SIZE*/) + 512)/1024 - ProgramSize - CFunctSizeK - FontSizeK /*- SavedVarSizeK - LibrarySizeK*/, ' ', 4, 10); strcat((char *)inpbuf, "K (");
IntToStrPad((char *)inpbuf + strlen((char *)inpbuf), 100 - ProgramPercent - CFunctPercent - FontPercent /*- SavedVarPercent - LibraryPercent*/, ' ', 2, 10); strcat((char *)inpbuf, "%) Free\r\n");
MMPrintString((char *)inpbuf);
//Get the library size
LibrarySizeK = LibraryPercent = 0;
LibraryMaxK= MAX_PROG_SIZE/1024;
if(Option.LIBRARY_FLASH_SIZE == MAX_PROG_SIZE) {
i = 0;
// first count the normal program code residing in the Library
p = LibMemory;
while(!(p[0] == 0 && p[1] == 0)) {
p++; i++;
}
while(*p == 0){ // the end of the program can have multiple zeros -count them
p++;i++;
}
p++; i++; //get 0xFF that ends the program and count it
while((unsigned int)p & 0b11) { //count to the next word boundary
p++;i++;
}
//Now add the binary used for CSUB and Fonts
if(CFunctionLibrary != NULL) {
j=0;
pint = (unsigned int *)CFunctionLibrary;
while(*pint != 0xffffffff) {
pint++; //step over the address or Font No.
j += *pint + 8; //Read the size
pint += (*pint + 4) / sizeof(unsigned int); //set pointer to start of next CSUB/Font
}
i=i+j;
}
LibrarySizeK=(i+512)/1024;
LibraryPercent = (LibrarySizeK * 100)/LibraryMaxK;
if(LibrarySizeK == 0) LibrarySizeK = 1; // adjust if it is zero and we have any library
if(LibraryPercent == 0) LibraryPercent = 1; // adjust if it is zero and we have any library
MMPrintString("\r\nLibrary:\r\n");
IntToStrPad((char *)inpbuf, LibrarySizeK, ' ', 4, 10); strcat((char *)inpbuf, "K (");
//IntToStrPad(inpbuf, (128*1024 + 512)/1024 - LibrarySizeK, ' ', 4, 10); strcat((char *)inpbuf, "K (");
IntToStrPad((char *)inpbuf + strlen((char *)inpbuf), LibraryPercent, ' ', 2, 10); strcat((char *)inpbuf, "%) "); strcat((char *)inpbuf, "Library\r\n");
IntToStrPad((char *)inpbuf + strlen((char *)inpbuf), LibraryMaxK-LibrarySizeK, ' ', 4, 10); strcat((char *)inpbuf, "K (");
IntToStrPad((char *)inpbuf + strlen((char *)inpbuf), 100 - LibraryPercent, ' ', 2, 10); strcat((char *)inpbuf, "%) Free\r\n");
MMPrintString((char *)inpbuf);
}
MMPrintString("\r\nSaved Variables:\r\n");
if(SavedVarCnt) {
IntToStrPad((char *)inpbuf, SavedVarSizeK, ' ', 4, 10); strcat((char *)inpbuf, "K (");
IntToStrPad((char *)inpbuf + strlen((char *)inpbuf), SavedVarPercent, ' ', 2, 10); strcat((char *)inpbuf, "%)");
IntToStrPad((char *)inpbuf + strlen((char *)inpbuf), SavedVarCnt, ' ', 2, 10); strcat((char *)inpbuf, " Saved Variable"); strcat((char *)inpbuf, SavedVarCnt == 1 ? " (":"s (");
IntToStr((char *)inpbuf + strlen((char *)inpbuf), SavedVarSize, 10); strcat((char *)inpbuf, " bytes)\r\n");
MMPrintString((char *)inpbuf);
}
IntToStrPad((char *)inpbuf, (( SAVEDVARS_FLASH_SIZE) + 512)/1024 - SavedVarSizeK, ' ', 4, 10); strcat((char *)inpbuf, "K (");
IntToStrPad((char *)inpbuf + strlen((char *)inpbuf), 100 - SavedVarPercent, ' ', 2, 10); strcat((char *)inpbuf, "%) Free\r\n");
MMPrintString((char *)inpbuf);
MMPrintString("\r\nRAM:\r\n");
IntToStrPad((char *)inpbuf, VarSize, ' ', 4, 10); strcat((char *)inpbuf, "K (");
IntToStrPad((char *)inpbuf + strlen((char *)inpbuf), VarPercent, ' ', 2, 10); strcat((char *)inpbuf, "%) ");
IntToStr((char *)inpbuf + strlen((char *)inpbuf), VarCnt, 10); strcat((char *)inpbuf, " Variable"); strcat((char *)inpbuf, VarCnt == 1 ? "\r\n":"s\r\n");
MMPrintString((char *)inpbuf);
IntToStrPad((char *)inpbuf, GeneralSize, ' ', 4, 10); strcat((char *)inpbuf, "K (");
IntToStrPad((char *)inpbuf + strlen((char *)inpbuf), GeneralPercent, ' ', 2, 10); strcat((char *)inpbuf, "%) General\r\n");
MMPrintString((char *)inpbuf);
IntToStrPad((char *)inpbuf, (CurrentRAM + 512)/1024 - VarSize - GeneralSize, ' ', 4, 10); strcat((char *)inpbuf, "K (");
IntToStrPad((char *)inpbuf + strlen((char *)inpbuf), 100 - VarPercent - GeneralPercent, ' ', 2, 10); strcat((char *)inpbuf, "%) Free\r\n");
MMPrintString((char *)inpbuf);
}
/*
* @cond
* The following section will be excluded from the documentation.
*/
/***********************************************************************************************************************
Public memory management functions
************************************************************************************************************************/
/* all memory allocation (except for the heap) is made by m_alloc()
memory layout is based on static allocation of RAM (very simple)
see the Maximite version of MMBasic for a more complex dynamic memory management scheme
|--------------------|
| |
| MMBasic Heap |
| (grows down) |
| |
|--------------------| <<< MMHeap
|--------------------|
| Variable Table |
| (grows up) |
|--------------------| <<< g_vartbl and DOS_vartbl
|--------------------|
| |
| Program Memory |
| (grows up) |
| |
|--------------------| <<< ProgMemory and DOS_ProgMemory
Calls are made to m_alloc() to assign the various pointers (ProgMemory, etc)
These calls must be made in this sequence:
m_alloc(M_PROG, size) Called whenever program memory size changes
m_alloc(M_VAR, size) Called when the program is running and whenever the variable table needs to be expanded
Separately calls are made to getmemory() and FreeHeap() to allocate or free space on the heap (which grows downward).
*/
void m_alloc(int type) {
switch(type) {
case M_PROG:
#ifdef rp2350
#ifndef PICOMITEWEB
if(PSRAMsize)memset((uint8_t *)PSRAMbase,0,PSRAMsize);
#endif
#endif
case M_LIMITED: // this is called initially in InitBasic() to set the base pointer for program memory
// everytime the program size is adjusted up or down this must be called to check for memory overflow
ProgMemory = (uint8_t *)flash_progmemory;
memset(MMHeap,0,heap_memory_size);
#ifdef GUICONTROLS
if(Option.MaxCtrls) Ctrl=(struct s_ctrl *)CTRLS;
#endif
break;
case M_VAR: // this must be called to initialises the variable memory pointer
// everytime the variable table is increased this must be called to verify that enough memory is free
memset(g_vartbl,0,MAXVARS * sizeof(struct s_vartbl));
break;
}
}
// get some memory from the heap
//void *GetMemory(size_t msize) {
// return getheap(msize); // allocate space
//}
// Get a temporary buffer of any size
// The space only lasts for the length of the command.
// A pointer to the space is saved in an array so that it can be returned at the end of the command
void __not_in_flash_func(*GetTempMemory)(int NbrBytes) {
if(g_StrTmpIndex >= MAXTEMPSTRINGS) error("Not enough memory");
g_StrTmpLocalIndex[g_StrTmpIndex] = g_LocalIndex;
g_StrTmp[g_StrTmpIndex] = GetSystemMemory(NbrBytes);
g_TempMemoryIsChanged = true;
return (void *)g_StrTmp[g_StrTmpIndex++];
}
// get a temporary string buffer
// this is used by many BASIC string functions. The space only lasts for the length of the command.
//void *GetTempStrMemory(void) {
// return GetTempMemory(STRINGSIZE);
//}
// clear any temporary string spaces (these last for just the life of a command) and return the memory to the heap
// this will not clear memory allocated with a local index less than g_LocalIndex, sub/funs will increment g_LocalIndex
// and this prevents the automatic use of ClearTempMemory from clearing memory allocated before calling the sub/fun
void __not_in_flash_func(ClearTempMemory)(void) {
while(g_StrTmpIndex > 0) {
if(g_StrTmpLocalIndex[g_StrTmpIndex - 1] >= g_LocalIndex) {
g_StrTmpIndex--;
FreeMemory((void *)g_StrTmp[g_StrTmpIndex]);
g_StrTmp[g_StrTmpIndex] = NULL;
g_TempMemoryIsChanged = false;
} else
break;
}
}
void MIPS16 ClearSpecificTempMemory(void *addr) {
int i;
for(i = 0; i < g_StrTmpIndex; i++) {
if(g_StrTmp[i] == addr) {
FreeMemory(addr);
g_StrTmp[i] = NULL;
g_StrTmpIndex--;
while(i < g_StrTmpIndex) {
g_StrTmp[i] = g_StrTmp[i + 1];
g_StrTmpLocalIndex[i] = g_StrTmpLocalIndex[i + 1];
i++;
}
return;
}
}
}
// test the stack for overflow - this is a NULL function in the DOS version
void __not_in_flash_func(TestStackOverflow)(void) {
// static uint32_t x=0xFFFFFFFF;
uint32_t y=__get_MSP();
// if(y<x){
// x=y;PIntH(x);PRet();
// }
if(y< HEAPTOP) error("Stack overflow, expression too complex at depth %",g_LocalIndex);
}
void MIPS64 __not_in_flash_func(FreeMemory)(unsigned char *addr) {
if(addr == NULL) return;
#if defined(rp2350) && !defined(PICOMITEWEB)
int bits;
if(PSRAMsize){
if(addr>(unsigned char *)PSRAMbase && addr<(unsigned char *)(PSRAMbase+PSRAMsize)){
do {
bits = SBitsGet(addr);
SBitsSet(addr, 0);
addr += PAGESIZE;
} while(bits != (PUSED | PLAST));
} else {
do {
bits = MBitsGet(addr);
MBitsSet(addr, 0);
addr += PAGESIZE;
} while(bits != (PUSED | PLAST));
}
} else {
do {
bits = MBitsGet(addr);
MBitsSet(addr, 0);
addr += PAGESIZE;
} while(bits != (PUSED | PLAST));
}
#else
int bits;
do {
bits = MBitsGet(addr);
MBitsSet(addr, 0);
addr += PAGESIZE;
} while(bits != (PUSED | PLAST));
#endif
}
void InitHeap(bool all) {
int i;
memset(mmap,0,sizeof(mmap));
memset(MMHeap,0,heap_memory_size+256);
#ifdef rp2350
if(all)memset(psmap,0,sizeof(psmap));
#endif
for(i = 0; i < MAXTEMPSTRINGS; i++) g_StrTmp[i] = NULL;
#ifdef PICOMITEVGA
WriteBuf=(unsigned char *)FRAMEBUFFER;
DisplayBuf=(unsigned char *)FRAMEBUFFER;
LayerBuf=(unsigned char *)FRAMEBUFFER;
FrameBuf=(unsigned char *)FRAMEBUFFER;
#else
FrameBuf=NULL;
WriteBuf=NULL;
LayerBuf=NULL;
#endif
}
/***********************************************************************************************************************
Private memory management functions
************************************************************************************************************************/
#ifdef rp2350
#ifndef PICOMITEWEB
unsigned int __not_in_flash_func(SBitsGet)(unsigned char *addr) {
unsigned int i, *p;
addr -= (unsigned int)PSRAMbase;
p = &psmap[((unsigned int)addr/PAGESIZE) / PAGESPERWORD]; // point to the word in the memory map
i = ((((unsigned int)addr/PAGESIZE)) & (PAGESPERWORD - 1)) * PAGEBITS; // get the position of the bits in the word
return (*p >> i) & ((1 << PAGEBITS) -1);
}
void __not_in_flash_func(SBitsSet)(unsigned char *addr, int bits) {
unsigned int i, *p;
addr -= (unsigned int)PSRAMbase;
p = &psmap[((unsigned int)addr/PAGESIZE) / PAGESPERWORD]; // point to the word in the memory map
i = ((((unsigned int)addr/PAGESIZE)) & (PAGESPERWORD - 1)) * PAGEBITS; // get the position of the bits in the word
*p = (bits << i) | (*p & (~(((1 << PAGEBITS) -1) << i)));
}
#endif
#endif
static inline __attribute__ ((always_inline)) unsigned int MBitsGet(unsigned char *addr) {
unsigned int i, *p;
addr -= (unsigned int)&MMHeap[0];
p = &mmap[((unsigned int)addr/PAGESIZE) / PAGESPERWORD]; // point to the word in the memory map
i = ((((unsigned int)addr/PAGESIZE)) & (PAGESPERWORD - 1)) * PAGEBITS; // get the position of the bits in the word
return (*p >> i) & ((1 << PAGEBITS) -1);
}
static inline __attribute__ ((always_inline)) void MBitsSet(unsigned char *addr, int bits) {
unsigned int i, *p;
addr -= (unsigned int)&MMHeap[0];
p = &mmap[((unsigned int)addr/PAGESIZE) / PAGESPERWORD]; // point to the word in the memory map
i = ((((unsigned int)addr/PAGESIZE)) & (PAGESPERWORD - 1)) * PAGEBITS; // get the position of the bits in the word
*p = (bits << i) | (*p & (~(((1 << PAGEBITS) -1) << i)));
}
#ifdef rp2350
#ifndef PICOMITEWEB
void __not_in_flash_func(*GetPSMemory)(int size) {
unsigned int j, n;
unsigned char *addr;
j = n = (size + PAGESIZE - 1)/PAGESIZE; // nbr of pages rounded up
for(addr = (unsigned char *)(PSRAMbase + PSRAMsize - PAGESIZE); addr >= (unsigned char *)PSRAMbase; addr -= PAGESIZE) {
if(!(SBitsGet(addr) & PUSED)) {
if(--n == 0) { // found a free slot
j--;
SBitsSet(addr + (j * PAGESIZE), PUSED | PLAST); // show that this is used and the last in the chain of pages
while(j--) SBitsSet(addr + (j * PAGESIZE), PUSED); // set the other pages to show that they are used
memset(addr, 0, size); // zero the memory
// dp("alloc = %p (%d)", addr, size);
return (void *)addr;
}
} else
n = j; // not enough space here so reset our count
}
// out of memory
TempStringClearStart = 0;
ClearTempMemory(); // hopefully this will give us enough to print the prompt
error("Not enough PSRAM memory");
return NULL; // keep the compiler happy
}
#endif
#endif
void MIPS64 __not_in_flash_func(*GetSystemMemory)(int size) { //get memory from the bottom up
int n=0, k;
unsigned char *addr;
k= (size + PAGESIZE - 1)/PAGESIZE; // nbr of pages rounded up
for(addr = MMHeap; addr < MMHeap + heap_memory_size - PAGESIZE; addr += PAGESIZE) {
if(!(MBitsGet(addr) & PUSED)) {
if(++n == k) { // found a free slot
k--;
MBitsSet(addr , PUSED | PLAST); // show that this is used and the last in the chain of pages
while(k--){
addr-=PAGESIZE;
MBitsSet(addr,PUSED);
}
memset(addr , 0, size); // zero the memory
return (void *)addr;
}
} else n = 0; // not enough space here so reset our count
}
TempStringClearStart = 0;
ClearTempMemory(); // hopefully this will give us enough to print the prompt
error("Not enough Heap memory");
return NULL; // keep the compiler happy
}
void MIPS64 __not_in_flash_func(*GetMemory)(int size) {
unsigned int j, n, k;
unsigned char *addr;
j = n = k= (size + PAGESIZE - 1)/PAGESIZE; // nbr of pages rounded up
for(addr = MMHeap + heap_memory_size - PAGESIZE; addr >= MMHeap; addr -= PAGESIZE) {
if(!(MBitsGet(addr) & PUSED)) {
if(--n == 0) { // found a free slot
j--;
MBitsSet(addr + (j * PAGESIZE), PUSED | PLAST); // show that this is used and the last in the chain of pages
while(j--) MBitsSet(addr + (j * PAGESIZE), PUSED); // set the other pages to show that they are used
memset(addr, 0, size); // zero the memory
return (void *)addr;
}
} else n = j; // not enough space here so reset our count
}
// out of memory
#ifdef rp2350
#ifndef PICOMITEWEB
if(PSRAMsize)return GetPSMemory(size);
#endif
#endif
TempStringClearStart = 0;
ClearTempMemory(); // hopefully this will give us enough to print the prompt
error("Not enough Heap memory");
return NULL; // keep the compiler happy
}
void *GetAlignedMemory(int size) {
unsigned char *addr=MMHeap;
while(((uint32_t)addr & (size-1)) && (!((MBitsGet(addr) & PUSED))) && ((uint32_t)addr<(uint32_t)MMHeap+heap_memory_size))addr+=PAGESIZE;
if((uint32_t)addr==(uint32_t)MMHeap+heap_memory_size)error("Not enough memory");
unsigned char *retaddr=addr;
for(;size>0;addr+=PAGESIZE, size-=PAGESIZE){
if(!(MBitsGet(addr) & PUSED)){
MBitsSet(addr,PUSED);
} else error("Not enough Aigned memory");
}
addr-=PAGESIZE;
MBitsSet(addr, PUSED | PLAST);
return(retaddr);
}
int FreeSpaceOnHeap(void) {
unsigned int nbr;
unsigned char *addr;
nbr = 0;
for(addr = MMHeap + heap_memory_size - PAGESIZE; addr >= MMHeap; addr -= PAGESIZE)
if(!(MBitsGet(addr) & PUSED)) nbr++;
#ifdef rp2350
#ifndef PICOMITEWEB
if(PSRAMsize){
for(addr = (unsigned char*)(PSRAMbase + PSRAMsize - PAGESIZE); addr >= (unsigned char*)PSRAMbase; addr -= PAGESIZE)
if(!(SBitsGet(addr) & PUSED)) nbr++;
}
#endif
#endif
return nbr * PAGESIZE;
}
int LargestContiguousHeap(void) {
unsigned int nbr;
unsigned char *addr;
nbr = 0;
for(addr = MMHeap; addr < MMHeap + heap_memory_size - PAGESIZE; addr += PAGESIZE){
if(!(MBitsGet(addr) & PUSED)) nbr++;
else break;
}
return nbr * PAGESIZE;
}
unsigned int UsedHeap(void) {
unsigned int nbr;
unsigned char *addr;
nbr = 0;
for(addr = MMHeap + heap_memory_size - PAGESIZE; addr >= MMHeap; addr -= PAGESIZE)
if(MBitsGet(addr) & PUSED) nbr++;
#ifdef rp2350
#ifndef PICOMITEWEB
if(PSRAMsize){
for(addr = (unsigned char*)(PSRAMbase + PSRAMsize - PAGESIZE); addr >= (unsigned char*)PSRAMbase; addr -= PAGESIZE)
if(SBitsGet(addr) & PUSED) nbr++;
}
#endif
#endif
return nbr * PAGESIZE;
}
int MemSize(void *addr){ //returns the amount of heap memory allocated to an address
int i=0;
int bits;
#ifdef rp2350
#ifndef PICOMITEWEB
if(addr>(void *)PSRAMbase && addr<(void *)(PSRAMbase+PSRAMsize)){
if(addr >= (void *)PSRAMbase && addr < (void *)(PSRAMbase + PSRAMsize)){
do {
bits = SBitsGet(addr);
addr += PAGESIZE;
i+=PAGESIZE;
} while(bits != (PUSED | PLAST));
}
return i;
} else {
if(addr >= (void *)MMHeap && addr < (void *)(MMHeap + heap_memory_size)){
do {
bits = MBitsGet(addr);
addr += PAGESIZE;
i+=PAGESIZE;
} while(bits != (PUSED | PLAST));
}
return i;
}
#else
if(addr >= (void *)MMHeap && addr < (void *)(MMHeap + heap_memory_size)){
do {
bits = MBitsGet(addr);
addr += PAGESIZE;
i+=PAGESIZE;
} while(bits != (PUSED | PLAST));
}
return i;
#endif
#else
if(addr >= (void *)MMHeap && addr < (void *)(MMHeap + heap_memory_size)){
do {
bits = MBitsGet(addr);
addr += PAGESIZE;
i+=PAGESIZE;
} while(bits != (PUSED | PLAST));
}
return i;
#endif
}
void *ReAllocMemory(void *addr, size_t msize){
int size=MemSize(addr);
if(msize<=size)return addr;
void *newaddr=GetMemory(msize);
if(addr!=NULL && size!=0){
memcpy(newaddr,addr,MemSize(addr));
FreeMemory(addr);
addr=NULL;
}
return newaddr;
}
void __not_in_flash_func(FreeMemorySafe)(void **addr){
if(*addr!=NULL){
if(*addr >= (void *)MMHeap && *addr < (void *)(MMHeap + heap_memory_size)) {FreeMemory(*addr);*addr=NULL;}
#ifdef rp2350
#ifndef PICOMITEWEB
if(*addr >= (void *)PSRAMbase && *addr < (void *)(PSRAMbase + PSRAMsize)) {FreeMemory(*addr);*addr=NULL;}
#endif
#endif
}
}
/* @endcond */