Blitz2D Intermediates: Using DataBanks
by Krylar

Introduction:

Before going any further, please note that this tutorial is written using the commercial version of BlitzBasic, version 1.50.

DataBanks are an interesting little tool that's kinda tough to describe. It's almost as if they're a mix between arrays, C-like pointers, and the old Poke/Peek tools of yesteryear.

Like arrays, they are definable/redefinable to reserve specific amounts of memory space and those spaces can be written to and read from. Unlike arrays, they are NOT type-specific...at least not in their current incarnation. Where you can setup an array to be of type Int or String or Float...or of TYPE, DataBanks are non-type-specific. DataBanks grab memory in byte-sized chunks only. But you can put most anything in those chunks.

Like C's pointers, you can create and free DataBanks at will throughout your program and then use data in those Banks in most anyway. Also, it has that pointer-like feel because of the way you need to ensure you're hitting the proper area of memory when using them (I'll get into this later in the article). DataBanks can point to other DataBanks too. Unlike C's pointers, DataBanks cannot be a pointer to simply anything. In other words, C's pointers can be configured to "point" to devices, and various pieces of memory without prior allocation. DataBank functions can only be used on previously allocated memory using the CreateBank command.

Like the old Poke/Peek tools of the Commodore era, you basically can use DataBank commands to "Poke" a value (place a value) into a particular memory address, or "Peek" at a value (read a value) sitting in a particular memory address. Unlike them, it apears the DataBanks can only Poke/Peek values from previously defined DataBanks using the CreateBank function as opposed to being able to Poke/Peek anywhere in memory.

Why Should I Use DataBanks?

Determining whether to use Arrays, Types, or DataBanks is based on what it is you're trying to accomplish. You can use all three in various, even overlapping, situations. But I can see using DataBanks for things such as encryption, compression, loading dynamically sized values such as map files, etc.

Basically, any place where you need to reserve memory in a DYNAMIC fashion, DataBanks can be very useful. Additionally, any place where you want that memory to be used and released (again dynamic) because you're trying to hide that data...again, things like encryption. It's not amazingly effective at hiding data, you'll still need to be cryptic if your goal is keep out prying eyes.

Plus remember that since you're storing on a byte basis, you can store characters, integers, floats, strings...as long as you don't go beyond the bounds of the Bank that you created using CreateBank.

How Do I Setup a DataBank?

Setting up a databank is quite easy. You simply call the CreateBank function with the number of bytes you wish it to allocate, and provide the function a handle to return the pointer to. Check this out:


DataBanks are NOT global by default, so you will need to put the BlitzBasic keyword "global" at the beginning of the line in order to make it global. This is a good thing because it means you can have DataBanks within functions that are hidden from other functions.

You could now fit 5 characters, or two shorts, or one integer...as long as it's not over 5 bytes, it'll fit.

As with most functions, you could also call CreateBank with variable data. So, let's assume that you have map loading functions that can load maps of varied sizes...like you'd want in a map editor. Using DataBanks, you could simply read in the first two integer values from the Map file itself. Treat the first number as the number of Columns in the map (or X) and the second number as the number of Rows (or Y). Then you would simply do the following:


The only issue here is that this will constrain the actual map data to byte sized portions. Huh? Since the value in the CreateBanks call is meant to allocate BYTES, not other types (such as Integers, Floats, etc.), BYTES are all that will be allocated. Now, this isn't necessarilly a bad thing. It just means that we can only have 256 unique tiles in our map file. This is because the highest value a byte can hold is 256. So how do you get higher values than that? You'll need to use either a SHORT or an INTEGER.

  • BYTE, 1 byte, 8 bits, maximum numeric value is 256
  • SHORT, 2 bytes, 16 bits, maximum numeric value is 65,535
  • INT, 4 bytes, 32 bits, maximum numeric value is 4,294,967,295

Now where I can see needing more than 256 differing tiles for a map...do you really think you'll need more than 65,535!?!?!? If so, I'm totally looking forward to playing your game...in like the year 2030. :)

Assuming that makes sense, then you'll want to be able to create a bank of SHORTS (or INTS). Here's how to do a SHORT:


Notice the only change is that I'm multiplying the product of MapColums * MapRows by 2. To hook up a bank of INTS you would multiply that product by 4.

How Do I Use a Previously Allocated DataBank?

There are two primary commands that do this, Peek and Poke, but there are a number of variations on those two commands. Here are all the Pokes and Peeks:

  • PokeByte
  • PokeShort
  • PokeInt
  • PokeFloat
  • PeekByte
  • PeekShort
  • PeekInt
  • PeekFloat
The main thing to keep in mind is that all banks are BYTE values. So, when you are Poking or Peeking values, you have to ensure that you are doing it in the proper Bank location. This can be confusing, so let me give an example:


This looks like it will put 3 "10"'s in the MapBank DataBank, and in effect...it will. But it won't do it the way we're expecting it to. What it will do is write "10" into the first and second byte on the first pass, but on the second pass we're only moving the DataBank pointer 1 over, so we'll be overwriting the 2nd byte in the first short in the second pass. Here's a graphic to help you visualize this issue:

The 0 - 5 are to represent the bytes in memory. The red bar shows where the number 10 will be written on the first pass of the FOR loop in or program. The green bar shows the 2nd pass, and the blue the 3rd pass.

As you can see, we're going to have overlapping values being written in and no value will be written in the bytes 4 and 5. This is because when we tell any of the Pokes (or Peeks) to write to a particular location, it literally writes starting at the location. It doesn't pay attention to whether or not there is already a value in that location...it just writes whatever you want and wherever you tell it.

Now here's another image that shows how we'd like the layout to look:

Notice that there are no overwritten values? This is good. Here's the re-written code that will do just that:


Notice all that I did was to multiply the BankLocation by 2, just like we did in the CreateBank section. That's all there is to it. And to read those values back, we'd do the reverse of that (using Peek). Here's the code:


That'll just print the value in the memory location, delay for a quarter of a second, and then go to the next value, etc. Try taking off the " * 2" from those and see what happens. I've never seen it crash, but I did get lot's of freaky numbers when Peek'ing ;)

The key point to remember is that you'll need to put a multiplier value in every reference to memory accessed via the DataBank functions that fits within the scope of the type of data you're storing or retreiving.

Do I Have to Free the Memory Allocated?

I haven't found any crashes from not freeing the memory, but it's a very good idea to free it if it's not being used. Besides, it's very simple to free the memory. All you have to do is call the FreeBank function with the name of the Handle you want to free. Here's an example:


Doesn't get much easier than that!

Any Example of Real Programs Using DataBanks?

In order to learn this, I took the Encryption/Decryption stuff I did in my last tutorial and converted it to use DataBanks. It was actually quite simple. To grab all the source and such: Click Here.

If you open the file "encrypt.bb" and do a search on "BankOffset", you'll see all of the places that the Bank commands are used. Compare those against the old way this was being done with arrays (by referencing the article "BlitzBasic Intermediates: Encryption/Decryption") and you'll find not too much in the way of difference.

Conclusion

There are many ways to use the DataBank functions and they offer a really powerful and exciting way to tackle a muriad of problems we, as coders, face all the time. Play around with these tools and see what you can make happen!

Until next time...cya!

-Krylar


For a printable copy of this article, please click HERE.


This site is Copyright© 2000-2004, BlitzCoder. All rights reserved.