Array anxiety

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!

Leave a Reply