Array bliss!

Wohoo!

So I found the ARRAY_READ and ARRAY_WRITE OP codes and I have just tried them out. I won’t make a long blog post about it now, I’ll just say that it seems to work fine.

First I saw the call to cMemoryResize and got concerned. But then I saw that it was encapsulated in an if statement so it would only be called if the index of the write was greater than the number of elements allocated. Ok, I’ll make this post a bit longer then, since you’re asking so kindly for it.

So this is the OP code I found, and it’s implementation:

/*! \page cMemory
 *
 *  <hr size="1"/>
 *  <b>     opARRAY_WRITE (HANDLE, INDEX, VALUE)  </b>
 *
 *- Array element write\n
 *- Dispatch status can change to FAILBREAK
 *
 *  \param  (HANDLER) HANDLE    - Array handle
 *  \param  (DATA32)  INDEX     - Index to element to write
 *  \param  (type)    VALUE     - Value to write - type depends on type of array\n
 *
 *\n
 *
 */
/*! \brief  opARRAY_WRITE byte code
 *
 */
void      cMemoryArrayWrite(void)
{
  DSPSTAT DspStat = FAILBREAK;
  PRGID   TmpPrgId;
  HANDLER TmpHandle;
  void    *pTmp;
  void    *pValue;
  DESCR   *pDescr;
  DATA32  Elements;
  DATA32  Index;
  void    *pArray;
  DATA8   *pData8;
  DATA16  *pData16;
  DATA32  *pData32;
  DATAF   *pDataF;

  TmpPrgId        =  CurrentProgramId();
  TmpHandle       =  *(HANDLER*)PrimParPointer();
  Index           =  *(DATA32*)PrimParPointer();
  pValue          =  PrimParPointer();

  if (cMemoryGetPointer(TmpPrgId,TmpHandle,&pTmp) == OK)
  {
    pDescr        =  (DESCR*)pTmp;
    if (Index >= 0)
    {
      Elements  =  Index + 1;

      DspStat   =  NOBREAK;
      if (Elements > (*pDescr).Elements)
      {
        if (cMemoryResize(TmpPrgId,TmpHandle,Elements) == NULL)
        {
          DspStat   =  FAILBREAK;
        }
      }
      if (DspStat == NOBREAK)
      {
        if (cMemoryGetPointer(TmpPrgId,TmpHandle,&pTmp) == OK)
        {
          pDescr      =  (DESCR*)pTmp;
          pArray      =  (*pDescr).pArray;
#ifdef DEBUG
          printf("  Write  P=%1u H=%1u     I=%8lu A=%8p\r\n",(unsigned int)TmpPrgId,(unsigned int)TmpHandle,(unsigned long)Index,pArray);
#endif
          switch ((*pDescr).Type)
          {
            case DATA_8 :
            {
              pData8          =  (DATA8*)pArray;
              pData8[Index]   =  *(DATA8*)pValue;
              DspStat         =  NOBREAK;
            }
            break;

            case DATA_16 :
            {
              pData16         =  (DATA16*)pArray;
              pData16[Index]  =  *(DATA16*)pValue;
              DspStat         =  NOBREAK;
            }
            break;

            case DATA_32 :
            {
              pData32         =  (DATA32*)pArray;
              pData32[Index]  =  *(DATA32*)pValue;
              DspStat         =  NOBREAK;
            }
            break;

            case DATA_F :
            {
              pDataF          =  (DATAF*)pArray;
              pDataF[Index]   =  *(DATAF*)pValue;
              DspStat         =  NOBREAK;
            }
            break;

          }
        }
      }
    }
  }
  if (DspStat != NOBREAK)
  {
#ifdef DEBUG
    printf("  WR ERR P=%1u H=%1u     I=%8lu\r\n",(unsigned int)TmpPrgId,(unsigned int)TmpHandle,(unsigned long)Index);
#endif
    SetDispatchStatus(DspStat);
  }
}

As you can see there is a scary realloc going on on line 3966, but it is only called if the array is too small to fit the index requested, whereas ARRAY WRITE_CONTENT always resize the array, even if the index is within bounds.

So I wrote up this little test code

vmthread  MAIN
{
	CALL( ClearLog )
	CALL( WriteLog, 'Hello rocktest 3!' )

	HANDLE hTemp
	ARRAY( CREATE8, 20, hTemp )

	CALL( WriteLog16, 'hTemp: ', hTemp )

	ARRAY_WRITE( hTemp, 0, 20 )
	ARRAY_WRITE( hTemp, 2, 22 )
	ARRAY_WRITE( hTemp, 1, 21 )

	DATA8 readApa

	ARRAY_READ( hTemp, 0, readApa )
	CALL( WriteLog8, 'Index 0: ', readApa )

	ARRAY_READ( hTemp, 1, readApa )
	CALL( WriteLog8, 'Index 1: ', readApa )

	ARRAY_READ( hTemp, 2, readApa )
	CALL( WriteLog8, 'Index 2: ', readApa )
}

I decided to only try the case I knew was broken with ARRAY_WRITE, and that is to write to an arbitrary index within bounds, and then write to a lower index and confirm that the contents of the higher index is still intact. And according to the log it works!

Compiling /Users/magnusrunesson/Projects/Rockhammer/rocktest.lms
539 bytes
Copied /Users/magnusrunesson/Projects/Rockhammer/rocktest.rbf to /media/card/rockhammer/rocktest.rbf
Starting '/media/card/rockhammer/rocktest.rbf'
Reading log from '/media/card/rockhammer/rocktest_log.txt'
Hello rocktest 3!
hTemp: 2
Index 0: 20
Index 1: 21
Index 2: 22

So great success! I know, I know, it is dangerous to be optimistic. So far I’ve mostly found out that stuff never works as well as they seem at first glance. But having looked through the code and written a test case I am optimistic about this.

Another positive thing about this is that the index you use is actually the index of the element, not the byte offset in memory.

So I’ll revisit my game code and make it work with this new array stuff, and let you know how it goes.

Funny how things just work out!

Leave a Reply