Array of BYTEs

Previous topic - Next topic

AndyH

Hi

How would I best go about storing level data for a scrolling tile map in memory with GLB?

I tried DIM tiles[100][100] but there are currently two things that concern me about this.

1) Having an array this large makes debugging unusably slow.  I've posted a bug about this in the bug forum.  Every press of F10 to trace the next line refreshes the Variables panel in the editor, and it takes a long time to do it when you have large arrays.  Hopefully this can be optimised though in a future build of GLB?

2) Storing an array of tiles using the GLB number data type is going to be wasteful on memory - I think each number is going to be either 64 bits or 32 bits on Windows/ PPC/ GP2X, meaning between 4 and 8 bytes per number required to store it.  If so I'd need 40,000 bytes to store a 100x100 tilemap (which isn't very big in game map terms).

All I need is bytes which for the same example requires only 10,000 bytes in total.  This will all add up.  

I see there is a OPENFILE - READBYTE  (BTW there's no mention of these commands in the contents under Commands by Category -> Input-Output) for reading bytes from a file, but I think we need to have a CREATEBANK( memory_size ) command with a POKEBYTE, POKEWORD, POKELONG and PEEKBYTE, PEEKWORD, PEEKLONG commands to read and write to this memory bank.   Unless there is a better way built in.

Kitty Hello

#1
New code: (needs V6 or later)
Code (glbasic) Select

// If this is not a seperate file, you must end the main() function first

// Just a quick test to ensure it's working
CREATEBANK(0, 128)
POKELONG(0, 0, 0x01234567)
POKEBYTE(0, 4, 0x70)
POKELONG(0, 5, 0xffff00ff)

LOCAL a%, b%, c%
a = PEEKLONG(0, 0)
b = PEEKBYTE(0, 4)
c = PEEKLONG(0, 5)

IF a=0x01234567 AND b=0x70 AND c=0xffff00ff
DEBUG "ok, works!"
ENDIF


FUNCTION _foo:
ENDFUNCTION

// Global variable for storing the bank pointers
INLINE
DGArray<unsigned char*> gMemBanks;
class MemBank_cleaner
{
   public:
   ~MemBank_cleaner()
   {
      for(int i=0; i<(int)LEN(gMemBanks); ++i)
         FREEBANK(i);
   }
} gMemBankClean;
ENDINLINE

FUNCTION CREATEBANK: index%, memsz%
INLINE
if(LEN(gMemBanks) <= index)
   REDIM(gMemBanks, index+1);
   gMemBanks(index) = new unsigned char[memsz];
ENDINLINE
ENDFUNCTION

FUNCTION POKEBYTE: ibank%, position%, value%
INLINE
   gMemBanks(ibank)[position] = (unsigned char)value;
ENDINLINE
ENDFUNCTION

FUNCTION PEEKBYTE%: ibank%, position%
INLINE
   return (int)gMemBanks(ibank)[position];
ENDINLINE
ENDFUNCTION

FUNCTION POKELONG: ibank%, position%, value%
INLINE
unsigned char* pBytes = &gMemBanks(ibank)[position];
   *((int*)pBytes) = value;
ENDINLINE
ENDFUNCTION


FUNCTION PEEKLONG%: ibank%, position%
INLINE
unsigned char* pBytes = &gMemBanks(ibank)[position];
   return *((int*)pBytes);
ENDINLINE
ENDFUNCTION


FUNCTION FREEBANK: ibank%
INLINE
   if(gMemBanks(ibank))
   {
      delete[] gMemBanks(ibank);
      gMemBanks(ibank) = 0L;
   }
ENDINLINE
ENDFUNCTION


FUNCTION WRITEBANK: ibank%, ifile%, ifrom%, ilen%
FOR i% = ifrom TO ifrom+ilen-1
WRITEBYTE ifile%, PEEKBYTE(ibank%, i%)
NEXT
ENDFUNCTION

FUNCTION READBANK: ibank%, ifile%, ifrom%, ilen%
LOCAL val%
FOR i% = ifrom TO ifrom+ilen-1
READBYTE ifile%, val%
POKEBYTE(ibank%, i%, val%)
NEXT
ENDFUNCTION




Old code:
Code (glbasic) Select
// If this is not a seperate file, you must end the main() function first
FUNCTION _foo:
ENDFUNCTION

// Global variable for storing the bank pointers
INLINE
DGArray<unsigned char*> gMemBanks;
class MemBank_cleaner
{
  public:
  ~MemBank_cleaner()
  {
     for(int i=0; i<(int)LEN(gMemBanks); ++i)
        FREEBANK(i);
  }
} gMemBankClean;
ENDINLINE

FUNCTION CREATEBANK: index, memsz
INLINE
if(LEN(gMemBanks) <= index)
  REDIM(gMemBanks, index+1);
  gMemBanks(index) = new unsigned char[(int)memsz];
ENDINLINE
ENDFUNCTION

FUNCTION POKEBYTE: ibank, position, value
INLINE
  gMemBanks(ibank)[(int)position] = (unsigned char)value;
ENDINLINE
ENDFUNCTION

FUNCTION PEEKBYTE: ibank, position
INLINE
  return (DGInt)gMemBanks(ibank)[(int)position];
ENDINLINE
ENDFUNCTION

FUNCTION FREEBANK: ibank
INLINE
  if(gMemBanks(ibank))
  {
     delete[] gMemBanks(ibank);
     gMemBanks(ibank) = 0L;
  }
ENDINLINE
ENDFUNCTION

(untested)

AndyH

Magic!  many thanks, will try out when I get home. :)

bigsofty

Handy for me too, THANKS! :D
Cheers,

Ian.

"It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration."
(E. W. Dijkstra)

Kitty Hello

If it works, I consider making these default commands.

AndyH

That would be the best solution to have it built in to the language - something I'm sure many people would like to use.  Will help with my collision map solution too :)

bigsofty

Banks are always handy beasts for large amounts of data manipulation.
Cheers,

Ian.

"It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration."
(E. W. Dijkstra)

AndyH

the INLINE code seems to work ok, just remove the third parameter (value) from the PEEKBYTE function.

Notice I have to start from bank 1 (not 0) and you have to be careful not to poke outside of your memory array.  Also guessing we'll get memory leaks if we don't free the bank up before stopping the app, or if creating banks on the same bank ID, the previous bank on that ID won't be destroyed?

Guess these will be ironed out when the in-built version of these commands are done :)

Moru

That sounds very handy, was a bit worried about this too for my future projects when I finaly have time for them :-)

Kitty Hello

OK, fixed. You can start with index 0 now, and the value is off.
When you quit an application, the "new" memory will be deleted automatically. You don't recursively allocate this memory, thus it's no memory leak. It's not nice, though.
Wait.... I'll insert a cleanup, too to make it safe.

AndyH

Thanks, lots of good stuff coming in the next update then :)

AndyH

With the new code above (in a separate file from the main source code) I get this error:


compiling:
C:\Users\AndyH\AppData\Local\Temp\glbasic\gpc_temp2.cpp: In function `void __static_initialization_and_destruction_0(int, int)':
C:\Users\AndyH\AppData\Local\Temp\glbasic\gpc_temp2.cpp:21: error: `__GLBASIC__::MemBank_cleaner::~MemBank_cleaner()' is private
C:\Users\AndyH\AppData\Local\Temp\glbasic\gpc_temp2.cpp:25: error: within this context
*** FATAL ERROR - Please post this output in the forum

Kitty Hello

fixed. Sorry.

AndyH

Thanks Gernot, this code is helping a lot.

That all works now on the PC, however the Create Bank command seems to lock up the Pocket PC.  I've created a test project.  Made a second source code file called MemBanks.gbas and copied your code into that.

In the main program source code I have put this:

Code (glbasic) Select
SETSCREEN 320,240,0
LIMITFPS 60


PRINT "About to create banks - mousewait",0,0
SHOWSCREEN
MOUSEWAIT

CREATEBANK(0, 100*100)

PRINT "Now to populate banks - mousewait",0,0
SHOWSCREEN
MOUSEWAIT

FOR y=0 TO 99
FOR x=0 TO 99
POKEBYTE(0, x+(y*10), RND(1))
NEXT
NEXT

PRINT "Now to PEEKBYTES - mousewait",0,0
SHOWSCREEN
MOUSEWAIT

FOR y=0 TO 10
FOR x=0 TO 10
PRINT PEEKBYTE(0, x+(y*10)), 20+x*6, 20+y*6
NEXT
NEXT

PRINT "Now to free banks - mousewait",0,0
SHOWSCREEN
MOUSEWAIT
FREEBANK(0)

PRINT "Done - mousewait",0,0
SHOWSCREEN
MOUSEWAIT
On the PC works fine.  On the Pocket PC it displays the About to create banks message, but click the mouse and it calls CREATEBANK but does not return and the program locks up.  Have to do a reset.

The very first version of your code (before you added the class) was working.  Any ideas?

AndyH

Found out what is having the problem on the PPC version:

Code (glbasic) Select
FUNCTION CREATEBANK: index, memsz
INLINE
if(LEN(gMemBanks) <= index)
   REDIM(gMemBanks, index+1);
   //FREEBANK(index);
   gMemBanks(index) = new unsigned char[(int)memsz];
ENDINLINE
ENDFUNCTION
Comment out the FREEBANK during the creation of the bank and it works.  Calling FREEBANK at the end of my app works too, just not from inside the CREATEBANK INLINE code.