Today I was going to create an array into which I would write handles to all existing game objects in the scene. So So I created all the functions I needed to create the array, find a free slot, add and remove game objects into the array, and a debug function to verify consistency.
What I found with the debug function was really strange though!
So I created the array, filled it with -1 (to indicate unused array entries) and printed the array. All entries came out -1, as expected.
Then I wrote the number 3 into the first entry of the array. I was expecting that all the other entries in the array would remain untouched, so I expected them all to be -1. But when I printed the entries they had all been set to 0. Very strange!
So I did an experiment. I only wrote the number 6 into the sixth entry in the array. The result was that entry 0-5 had kept their values, so they were -1. The sixth entry had the value 6. All the following entries had the value 0.
I started suspecting that WRITE_CONTENT would fill the rest of the array with garbage so my next experiment was to first write the value 7 into index 7, and value 6 into index 6. As expected the array was -1 up to entry 5, entry 6 had the value 6 and all following entries was 0. I was onto something.
However, my next test would confuse me. My next test sequence was:
Write value 6 into entry 6
Write value 5 into entry 5
Write value 7 into entry 7
Because my theory was that WRITE_CONTENT was writing trailing zeroes whenever I wrote something to the array I expected the array to look like:
-1
-1
-1
-1
-1
5
0
7
0..
But instead the array was:
-1
-1
-1
-1
-1
5
6
7
0..
So now it looked like the array worked properly.
I went back to writing 6 to 6 and 5 to 5, and it didn’t work.
I had a few ideas of what it could be but none of them fitted and I couldn’t really find a pattern to what was going on. My next test sequence was
Write 0 to entry 0
Write 6 to entry 6
Write 5 to entry 5
Write 7 to entry 7
The result looked like this:
1
-1
-1
-1
33
5
6
7
0..
Now a completely random (to me) value had appeared in the array. 33.
It was a this point I gave up and started looking at the source. I’m generally too lazy to look at the source, but sometimes things just doesn’t make sense I don’t have any choice. So this is the source:
case WRITE_CONTENT : { PrgId = *(DATA16*)PrimParPointer(); TmpHandle = *(DATA16*)PrimParPointer(); Index = *(DATA32*)PrimParPointer(); Bytes = *(DATA32*)PrimParPointer(); pDData8 = (DATA8*)PrimParPointer(); DspStat = FAILBREAK; #ifdef DEBUG printf("ARRAY WRITE_CONTENT CP=%d PP=%d\r\n",TmpPrgId,PrgId); #endif if (PrgId == (PRGID)CURRENT_SLOT) { PrgId = TmpPrgId; } if (cMemoryGetPointer(PrgId,TmpHandle,&pTmp) == OK) { ElementSize = (DATA32)(*(DESCR*)pTmp).ElementSize; if (ElementSize) { Elements = (Index + Bytes + (ElementSize - 1)) / ElementSize; ISize = Elements * ElementSize; pTmp = cMemoryResize(PrgId,TmpHandle,Elements); if (pTmp != NULL) { if ((Index >= 0) && (pDData8 != NULL)) { pData8 = (DATA8*)pTmp; Data32 = 0; while ((Data32 < Bytes) && (Index < ISize)) { pData8[Index] = pDData8[Data32]; Data32++; Index++; } DspStat = NOBREAK; } } } } } break;
The keen of you will notice there is a cMemoryResize() call in there. I was naĂŻvely assuming it would only resize the array if Elements was too great to fit all the data to be written to the array, but I looked into it just to be safe.
void* cMemoryResize(PRGID PrgId,HANDLER TmpHandle,DATA32 Elements) { DATA32 Size; void *pTmp = NULL; if (cMemoryGetPointer(PrgId,TmpHandle,&pTmp) == OK) { Size = Elements * (*(DESCR*)pTmp).ElementSize + sizeof(DESCR); pTmp = cMemoryReallocate(PrgId,TmpHandle,(GBINDEX)Size); if (pTmp != NULL) { (*(DESCR*)pTmp).Elements = Elements; } #ifdef DEBUG printf(" Resize P=%1u H=%1u T=%1u S=%8lu A=%8p\r\n",(unsigned int)PrgId,(unsigned int)TmpHandle,(unsigned int)MemoryInstance.pPoolList[PrgId][TmpHandle].Type,(unsigned long)MemoryInstance.pPoolList[PrgId][TmpHandle].Size,MemoryInstance.pPoolList[PrgId][TmpHandle].pPool); #endif } if (pTmp != NULL) { pTmp = (*(DESCR*)pTmp).pArray; } return (pTmp); }
As you can see it always go to cMemoryRealloc() so let’s have a look at that
void* cMemoryReallocate(PRGID PrgId,HANDLER Handle,GBINDEX Size) { void *pTmp; pTmp = NULL; if ((PrgId < MAX_PROGRAMS) && (Handle >= 0) && (Handle < MAX_HANDLES)) { if ((Size > 0) && (Size <= MAX_ARRAY_SIZE)) { if (cMemoryRealloc(MemoryInstance.pPoolList[PrgId][Handle].pPool,&pTmp,(DATA32)Size) == OK) { MemoryInstance.pPoolList[PrgId][Handle].pPool = pTmp; MemoryInstance.pPoolList[PrgId][Handle].Size = Size; } else { pTmp = NULL; printf("cMemoryReallocate out of memory\r\n"); } } } #ifdef DEBUG if (pTmp != NULL) { printf(" Reallocate P=%1u H=%1u S=%8lu A=%8p\r\n",(unsigned int)PrgId,(unsigned int)Handle,(unsigned long)Size,MemoryInstance.pPoolList[PrgId][Handle].pPool); } else { printf(" Reallocate error P=%1u H=%1u S=%8lu\r\n",(unsigned int)PrgId,(unsigned int)Handle,(unsigned long)Size); } #endif return (pTmp); }
This in turn is also just doing some sanity checks before entering cMemoryRealloc()
RESULT cMemoryRealloc(void *pOldMemory,void **ppMemory,DATA32 Size) { RESULT Result = FAIL; *ppMemory = realloc(pOldMemory,(size_t)Size); if (*ppMemory != NULL) { Result = OK; } return (Result); }
As you can see cMemoryRealloc always go to stdlib realloc.
Soo, if I’m not missing anything, WRITE_CONTENT will ALWAYS reallocate the array, and use the realloc() call to move data from the old block to the new. So that explains a lot.
I don’t have a plan for how to handle arrays yet, but it seems like arbitrarily writing into an array isn’t supported by the VM. I will have to dig in deeper to see if there are any work arounds, but it seems like writing to an array isn’t as straight forward as I would have hoped.
I’m still not on top of the LMS syntax but I have the @ and & in mind. Perhaps they can be of help here. Maybe I don’t have to use the bulky READ_CONTENT and WRITE_CONTENT, maybe those are just for writing big amounts of data into an array. I still think I need to use arrays to allocate memory though.
Trying out handles and addresses will be my next project!