Local and Global Variables
Using the stack for fun and profit

The Issue with Registers
We've learned a bunch of assembly already, and there's one clear problem that stares us in the face. You are free to use the registers EAX, ECX, and EDX. But there aren't many other places where you can put your own numbers.

What about RAM offsets? Can't you store your own numbers into a location like DWORD [49E6D0]? Yes, you can, but then you'd be messing with the player's maximum health because DWORD [49E6D0] holds the max-health. It would not be very fun for the player if you decided to use DWORD [49E6D0] to temporarily store the number 0.

The registers are few in number. There are only 3 you can use on a regular basis since the other registers are reserved for special reasons. Memory locations are large in number, but nearly all of them serve some purpose that's important to the game (such as booster fuel or number of whimsical stars). We need an easy way to store large lists of our own numbers without screwing the game up. This is where local variables come in.

Defining Local and Global
Local and global have easy definitions. Local means that something is not accessible by everyone. Therefore, something that's local can only be accessed by a few people. Global means that something is accessible by (practically) everyone.

Let's assume that my grandmother owns a cookie shop. Our family has named the shop, creatively enough, "Grandma's Cookie Shop". This cookie shop is one of a kind: you won't find my grandmother's cookies baked anywhere else in the world. Grandma's cookie shop is a local business.

Let's also assume that my city has a fast food restaurant. We'll call it Burger Palace[1]. Burger Palace is a well-known fast food chain, and there are many of these restaurants all across the globe. Burger Palace can be called a global business.

My computer is a local machine. I'm the only one who has the password, and so only I have access to my own computer. However, I can also connect to the Internet, which is a global network. People all over the world use the Internet, and the web pages on the Internet can be accessed by many people across the globe.

DWORD [49E6D0], which is Quote's maximum health, is a global variable. The value of DWORD [49E6D0] can be accessed and modified by practically any function inside the Cave Story .exe file. All RAM offsets are global variables. Other variables may include local variables. In most cases, access to local variables is limited to the function in which those variables were declared. We'll see examples of this soon.

Using the Stack
Now, of course we're going to use the stack to store our local variables, because it conveniently acts as a giant list of numbers. You already understand how to use PUSH and POP, but these two instructions won't be too useful because they only work with the top of the stack. We need to dig into the middle of the stack. Sounds familiar?

It should be. We know that function arguments are kept inside [EBP+8], [EBP+C], [EBP+10] ... and so on, all of which are DWORD-sized memory locations somewhere within the stack.

Today, instead of adding a value to EBP using a plus sign, we are going to subtract a value from EBP using a minus sign. All of this will still be done in square brackets, of course. The local variables are located inside [EBP-4], [EBP-8], [EBP-C], [EBP-10], and so on.

Allocating Local Variables
To allocate local variables so that you can use them, you should figure out the (number of local variables you need), and multiply that number by 4. Then you need to put SUB ESP,(that number) right after the beginning of your function:
Example Function
PUSH EBP               ;--- These two lines of
MOV EBP,ESP            ;--- code start the function.
SUB ESP,C              ;In hex 3 times 4 = C. We allocated 3 local variables.
MOV EAX,200            ;Store a number to a regular register.
MOV DWORD [EBP-4],68   ;Store a number to a local variable.
ADD DWORD [EBP-4],7    ;Add a number to that local variable.
MOV DWORD [EBP-8],99B  ;Store another number to a different local variable.
MOV EAX,DWORD [EBP-4]  ;Store a local variable's value to a register.

;pretend there is more code here.

MOV ESP,EBP            ;--- These three lines of
POP EBP                ;--- code will exit the
RETN                   ;--- function.
How does subtracting a number from ESP give us the ability to use local variables?
ESP points to the top slot of the stack. It actually measures the depth of the stack - when ESP is big, you are lower in the stack. When ESP is small, you are higher in the stack. So by making ESP smaller (subtracting a number from it), we actually increase the size of the stack by a certain number of slots.

This is what happens if you use SUB ESP,10 to allocate 4 local variables. Remember that 4*4 = 10 in hex, so that's why 4 variables would be created.

Local Variables

After SUB ESP,10 is finished executing, we see that we can still access function arguments through [EBP+8], [EBP+C], and so on. We can access our local variables by using [EBP-4], [EBP-8], and so on.

Using Local Vars to Preserve Register Values
MOV EDX,DWORD [EBP+8]     ;Put the NPC object pointer into EDX.
MOV DWORD [EDX+14],130    ;Store 130 to Y-velocity.
MOV DWORD [EBP-4],EDX     ;Save the value of EDX.
CALL 420640               ;Play a sound.
MOV EDX,DWORD [EBP-4]     ;Load the value of EDX.
When you call a function, the values of EAX, ECX, and EDX are used, and probably none of them will be preserved.
You can use local variables to get around this. Here we want to save the value of EDX, so we put it into a local variable and then store that local variable back to EDX after we call the "play sound" function.

Remember that there are many ways to preserve register values before and after a function CALL. You could do PUSH EDX before and then POP EDX after. You could just do MOV EDX,DWORD [EBP+8] after the function call because the value of [EBP+8] has not changed. Here we chose to use local variables, but of course that's only one of the possible solutions.

When Local Variables are "Erased"
Every function has its own local variables. Functions usually do not share the contents of their local variables unless those variables get PUSHed right before CALLing a different function (then the values will be passed as arguments into the other function).

What happens when a function ends? In other words, what happens when a function hits the RETN statement? The local variables are erased. This happens because the local variables will soon be overwritten with new data, so the old values inside those variables are not preserved.

But notice that whenever you CALL a function, the SUB ESP,(number) part will be executed again. So that means you always start fresh with a new set of local variables right after the function begins. Of course you may use them - but the old values won't be there anymore. Just use MOV to store some new values to them.

Previous Lesson: The <BBP Command
Next Lesson: Framerects
Table of Contents

[1]Burger Palace is not a real fast food chain. It's a fictional one that I invented for the purposes of this guide. Okay fine - if you search for "Burger Palace" on the web, you will find a real restaurant, but that's just a coincidence!