/*********************************************************************************************************************** PicoMite MMBasic Memory.c Geoff Graham, Peter Mather Copyright (c) 2021, 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 . 5. Neither the name of the 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 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 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 #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; unsigned char *MMHeap=AllMemory; #else unsigned char __attribute__ ((aligned (256))) AllMemory[HEAP_MEMORY_SIZE+256]; unsigned char *MMHeap=AllMemory; uint32_t framebuffersize=0; unsigned char *FRAMEBUFFER=NULL; #endif #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; uint32_t framebuffersize=0; unsigned char *FRAMEBUFFER=NULL; #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 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 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/sizedestinationsize)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> 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=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=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(from0){ *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(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 */