Making arrays in GB Studio with indirection.

Use indirection GBVM instructions to make arrays of global variables.

Introduction.

Arrays are a frequently requested feature for GB Studio, but there are actually a couple of ways to create arrays already. One such method is using the indirection GBVM instructions to treat a segment of global variables as if it is an array. This article will explain how to make one or more arrays in GB Studio with GBVM and the indirection method.

What are the indirection GBVM instructions?

In computer science, indirection refers to indirectly referencing a variable by something other than its value, such as its index. That’s what you’re doing when you get an element of an array by referencing its index. The indirection GBVM instructions VM_SET_INDIRECT and VM_GET_INDIRECT allow you to reference a global variable by its index.

VM_SET_INDIRECT.

VM_SET_INDIRECT sets the value of a global variable at a given index. It takes two parameters. The first parameter is the index of the global variable to set, and can be either a global variable or a value on the VM stack. The second parameter is the value to set the global variable to, and can also be either a global variable or a value on the VM stack.

The following is an example of setting the global variable at index 16 to the value 8.

VM_PUSH_CONST 16 VM_PUSH_CONST 8 VM_SET_INDIRECT .ARG1, .ARG0 VM_POP 2

Don’t forget that you could replace either .ARG0 or .ARG1 with global variables instead of using immediate values on the stack, like in the example below.

VM_SET_INDIRECT VAR_MY_INDEX, VAR_MY_VALUE

VM_GET_INDIRECT.

VM_GET_INDIRECT gets the value of a global variable at a given index and stores it in another variable. It takes two parameters. The first parameter is the variable to store the value in, and can be either a global variable or a value on the VM stack. The second parameter is the index of the global variable to get, and can also be either a global variable or a value on the VM stack.

The following is an example of getting the value of the global variable at index 16 and storing it in another global variable with the symbol VAR_GOT_VALUE.

VM_PUSH_CONST 16 VM_GET_INDIRECT VAR_GOT_VALUE, .ARG0 VM_POP 1

Like with VM_SET_INDIRECT, you can replace stack values with global variables and vice versa.

Using indirection to make arrays.

At this point, if you have some programming experience, you probably understand how you can use indirection to make simple arrays.

Because indirection allows us to reference global variables by their index, we can treat all of the 512 global variables as a single large array. Each global variable is an element of that array, starting with variable 0 at index 0, and ending with variable 511 at index 511.

Perhaps more importantly, we can also treat any segment of global variables as its own array. If we only need an array of length 16, we can select any run of 16 variables as our array, and apply an appropriate offset to the index when referencing that array. For example, if we want our array of 16 length to begin at variable 100, we add an offset of 100 when referencing it. Index 0 is now at index 100, index 1 is now at index 101, index 2 is now at index 102, and so on.

A small child's visualisation of how the large array of all 512 global variables can be treated as smaller subarrays.

Making array offsets automatic.

It might be useful to store an array’s offset in a global variable so you can move the position of your array later on. You then add a GBVM snippet that automatically adds the offset to indices when using indirection. Automatic offsets also mean that you don’t have to manually add the offset to indices when writing code.

VM_RPN .R_REF VAR_ARRAY_OFFSET .R_REF VAR_ARRAY_INDEX .R_OPERATOR .ADD .R_STOP VM_GET_INDIRECT VAR_GOT_VALUE, .ARG0 VM_POP 1

You also have the option to automatically start your array at the first unused global variable by using MAX_GLOBAL_VARS as your offset. MAX_GLOBAL_VARS is an engine value that represents the number of global variables you have used in your project, so using it as your offset will automatically start your array at the first unused global variable.

VM_RPN .R_INT16 MAX_GLOBAL_VARS .R_REF VAR_ARRAY_INDEX .R_OPERATOR .ADD .R_STOP VM_GET_INDIRECT VAR_GOT_VALUE, .ARG0 VM_POP 1

The practicality of indirection.

Personally, I haven’t used GBVM indirection in any published games. I’m not sure how performant it is, so if you have a use case where performance is likely to be an issue, please do your own tests before committing to it. In my tests, looping over a small array with a lot of GBVM instructions per element can take more than a second. In some cases such as puzzle games, you might need to delegate any behaviour that loops over arrays to engine code. So I can provide more information on its practicality, I'd love to hear what you’ve tried with GBVM indirection. Please don’t hesitate to reach out!

Guide by Shin. Last updated 30/01/2024.

Read these next.