In this chapter we are going to be looking at using the stack. We’ll cover pushing stuff onto the stack, as well as popping it back off. As we learn more and more about the stack, you’ll see why it’s so important that we use the it. If you don’t know what the stack is right now, or don’t have a really good understanding of, then before you continue reading this chapter you really need to learn about it.
Stack Pointer
The stack pointer is the address of the last thing that was pushed onto the stack. Another way to think about it is that the stack pointer is the address of the top of the stack. In PowerPC, the stack pointer is r1. So whenever you want to push/pop things to/from the stack, you’re going to need to use r1. Let’s just take a look at a diagram to clear up any confusion.
Pretty simple huh? Now let’s talk about pushing somthing onto the stack, specifically the contents of r3, which is 4 bytes in size. The instruction that we’ll use to actually push the contents of r3 onto the stack is one that we’ve already looked at, stw. So this means that the first thing that we need to figure out is the address at which we’ll be storing r3 at. Well, we know that r1 stores the address of the last thing we pushed onto the stack. However, we cannot just store r3’s contents at this address because there is something already being stored there. Since we’re pushing something onto the stack, essentially what we want to do, is make the bytes of r3 come right before the bytes of whatever was previously pushed on the stack. So we want the stack to look like this:
From the above picture, you can see that the address of the bytes we’re storing will be at a lower address than the stuff previously pushed onto the stack. How much lower? Well, we want to make just enough room for the contents of r3 to be stored, and since r3 is 4 bytes, we need to back up 4 bytes. So the address that we’ll be storing the contents of r3 at, is the stack pointer minus four, aka r1 - 4. Now for the actual code.
stw r3, -4(r1)
Here, we are simply storing the contents of r3 at the address of the stack pointer minus 4. This way, the contents of r3 will be just above whatever was previously stored on the stack. After this code executes, 1r won’t point to the last thing pushed onto the stack. It’ll store the address of whatever was pushed on before we pushed r3. So we need to update r1 to point to the top of the stack again. One way of doing this is simply subtracting 4 from r1. So the complete proper code to push r3 on the stack would look like this.
stw r3, -4(r1)
subi r1, r1, 4
There is an easier way to do it though. Instead of using the instruction stw, we can use the instruction stwu which stands for store word and update. This instruction will simply add the offset to the address provided after it stored whatever you wanted it to store. The following diagram should give you a pretty good understanding of what the instruction does.
So instead of using two instructions, we could simply do the following to store r3 and update the stack pointer.
stwu r3, -4(r1)
Now that we know how to push stuff onto the stack, let’s talk about popping stuff off. So let’s say that we want to pop r3 off the stack, assuming we just pushed it on. To do that, we’d simply use the lwz instruction, which is one we’ve already gone over. The address of the top-most thing on the stack is in r1, so to pop off the integer that we previously pushed on, we just need to load the word at the address in the stack pointer. So to load the contents of r3, assuming we just pushed them on, we’d do the following.
lwz r3, 0(r1)
We are simply loading r3 with the top-most integer on the stack. So basically what we’re doing is popping a value from the stack into r3. Since we popped a value from the stack, we need to change the value of the stack pointer so that it will point to the value after the one we just popped off. All that we need to do is add four to r1. So the full code to pop a value off of the stack is as follows.
lwz r3, 0(r1)
addi r1, 4
Unfortunatly there is no shorter way to do this, that I know of. So everytime that you pop something off of the stack, you need to add the size of it to the stack pointer. We are going to continue looking at the stack for a little bit. We’ll take a look at pushing and popping shorts and chars. First, we’ll take a look at pushing a short onto the stack and then popping it back off. So let’s say that we wanted to push the short in r3 onto the stack, and then pop it off into r4. So to do that, we’d use the following code.
sthu r3, -2(r1)
lhz r4, 0(r1)
addi r1, r1, 2
The first thing that we do here push the short in r3 onto the stack. We only need to go 2 bytes before the top of the stack because the size of a short is only 2 bytes. Rigth after we push the value onto the stack, the next instruction is to pop it right back off. Since we did sthu and not sth, r1 still stores the address of the top of the stack. This means that the address in r1 is the address of the short that we just pushed onto the stack. So when we load the short at the address in r1, we’ll be loading the short we just pushed onto the stack. Finally, we just need to add 2 to the stack pointer so that it points to the top of the stack once again.
Now let’s run through a quick example of pushing and popping a character from the stack. This time we’ll say that the char we want to push is in r4. So to push r4 onto the stack and pop it off into r3, we’d do something like this.
stbu r4, -1(r1)
lbz r3, 0(r1)
addi r1, r1, 1
The first thing that we do here is push the byte in r4 onto the stack. We back up one byte because the size of a character is only one byte. After we push the character onto the stack, we pop it right back off. Since we did stbu, it updated the value of r1 to the address of the byte we just pushed on the stack. So when we go and read the byte at the address in r1, we are reading the character that we previously pushed on the stack. Finally, after we pop the value from the stack, we need to update the value of the stack pointer so that it once again points to the top of the stack.
Now let’s take a look at pushing multiple things onto the stack. First, we’ll push a character and then an integer onto the stack. We’ll say that the character that we want to push is in r3 and the integer that we want to push is in r4. The code to do that would look something like this.
stbu r3, -1(r1)
stwu r4, -4(r1)
After the code has executed, the stack will look something like this:
The first thing that we do in the code, is push the character in r3 onto the stack. Since we used the stbu instruction, it will update the value in r1 to store the address of the character we just pushed on the stack. Right after we push the character on, we push the integer on. We go -4 since an integer is 4 bytes in length, and again we used the stwu instruction, so it’ll update the stack pointer for us. We pushed the character on first, and then we pushed the integer on, so the integer will be at higher address than the character. In the stack, the integer is on top of the character. Notice how we didn’t put any padding to get the integer at an address that is a multiple of four, unlike structs. We don’t need to do this for the stack because we, the programmers, will be the ones accessing the stack, not our computer. If you want to add padding you can, I just prefer not to.
Let’s say that we wanted to pop that stuff off, the integer and the character. Since the integer is on top, we’ll pop that off first, and then we’ll pop off the character. So do that, we’d simply do the following.
lwz r4, 0(r1)
lbz r3, 4(r1)
addi r1, r1, 5
First, I just read the integer off the top of the stack into r4. Right after that I read the character. Since I didn’t change the value of the stack pointer, it still points to the integer. In order to get the address of the character after it, I have to go 4 bytes further, so that it skips over the integer. After I read the two values, I add 5 to r1 so that it’ll point to the stuff on the stack before the character, this way we are popping them off. I could have added to the stack pointer rigth after I read each value, however doing so would make there be four instructions instead of three. If I were to that, it would look like this:
lwz r4, 0(r1)
addi r1, r1, 4
lbz r3, 0(r1)
addi r1, r1, 1
It doesn’t really matter how you do it, but I prefer the first way. If it makes more sense to you to do it the second way, then do it that way, it doesn’t really matter.
Why Use the Stack?
There are a few different reasons to use the stack. If you were to run out of registers when doing something, then you could simply push a bunch of them onto the stack, and then pop them off for later usage. Also, when you’re calling other functions you have to push values on to the stack to preserve their values, but we’ll get into that in the next chapter.