The Count Register
The count register is 32 bit register that is most commonly used to hold the counter for a loop, although you can use it for anything you want. Using the counter register, or CTR, is easier than using a GPR to hold a loop count, and you’ll see why this is as we move along. You cannot load the CTR, aka the count register, with a value like you would a GPR. You cannot use the li instruction to place a value in the CTR, instead you need to use an instruction called mtctr, which stands for move to CTR. This instruction takes one operand, and it is the value that you want to put in the CTR, and it must be a register, it cannot be an immediate value. So if we wanted to load the CTR with the value 10, then we’d have to do something like this:
li r3, 10
mtctr r3
Since we cannot give the mtctr instruction an immediate value, we must first load the immediate value into a register, and then give that register to the mtctr instruction.
If you ever wanted to perform an operation on the CTR, like adding, subtracting etc., you’d have to do it indirectly. You’d have to read the value from the CTR into a GPR, perform the operation, and then put the value back into the CTR. To get the value in the CTR, we can simply use the mfctr instruction which stands for move from CTR. This instruction takes one operand, it is the destination register, so the register that the CTR’s value will be copied to. If we wanted to add 7, to the CTR, we’d have to do something like this:
mfctr r3
addi r3, r3, 7
mtctr r3
First, we copy the value in the CTR to r3, then we simply add 7 to r3, then we copy r3’s value back into the CTR.
Looping with the CTR
Looping with the count register is really easy thanks to a little instruction called bdnz which stands for branch decrement not zero. This instruction will decrement the count register, and then it will only branch to the address supplied as the first operand if the value of the count register, after being decremented, isn’t 0. Like I mentioned above, the bdnz instruction takes one operand, it is the address that it will potentially branch to. So let’s look at a loop.
li r3, 10
mtctr r3
loopTop:
addi r4, r4, 5
bdnz loopTop
The first thing that we do, is put 10 into the CTR, then it adds 5 to whatever value is in r4, then it comes down to the bdnz instruction, here, it decrements the CTR, so the value of the CTR is now 9, then it will only branch to the label loopTop, if the value in the CTR isn’t 0. Since the value in the CTR is 9. So it will loop through that addi instruction 10 times. Although that code is pointless, I hope that helped you understand looping in PPC.
Let’s take a look at a more practical use of looping in PowerPC. We’ll make a function that will generate the first n numbers in the Fibonacci sequence, and store those numbers in an array, that is passed in as a parameter. So the prototype of the function would look like this:
Now let’s look at the function itself.
The first thing that it does is load r5 with the first value in the sequence, which is 0, and then it loads r6 with the second value in the sequence, which is 1. After this instruction, it does different things depending on the value of n. So I’m going to go through each value of n individually.
If n is 1
The next instruction it has to execute is the cmpwi instruction. Here, we are comparing n with 1. If n is 1, then we only need to put the first number in the sequence in the destination array. The instruction after the comparison will branch if n equals 1, which it does. So it goes down to the store1 label, and it simply stores the first number in the sequence in the array. Since there is nothing else for it to do, it returns.
If n is 2
Just like above we first compare n with 1. Since n isn’t equal to 1, it won’t branch to that label, it’ll just continue on. The next couple instruction will store the first two numbers in the sequence in the array called dest. After storing the first two numbers in the sequence, we check and see if there is anymore work to be done. If n is 2, then there is nothing else for us to do, we already “generated” the first two numbers in the sequence. So we will use the cmpwi instruction to compare n with 2. If they are equal, then we will return, well we’ll branch to a label that will return.
If n is greater than 2
We will say that n is 6 in this example. First, we must compare n with 1, to see if we only need to generate one number, since n is greater than 1, it won’t branch to that label. So just like above, it’ll store the first two numbers in the sequence in the array. Right after that it’ll compare n with 2 to see if anything else needs to be done. Since n is greater than 2, it’ll continue on, because it needs to generate the rest of the numbers. After the comparison, we subtract 2 from n since we already generated and stored the first two numbers in the sequence. After that, we put n into the CTR so that we loop the correct number of times later on. After we set up the count register, we add 8 to r4, which is the destination array. We do this so that r4 now stores the address of the next element in the array, since the first two are filled up with the first two Fibonacci numbers. If this is confusing, take a look at the following diagram.
As you can see from the diagram, all we’re doing is having r4 store the address of the next element in the array. Once we fix the array, we will generate the next number in the sequence, and this is really easy to do. To get the next number, we simply calculate the sum of the previous two. So we just add r5 and r6, to get the next number in the sequence. Then, we make the old first value the new first value, and we make the old first value, the new calculated value. So we are basically just moving along the sequence, generating the next number as we go. Maybe it’d help to look at a picture.
After we generate the next value, we store it in the array. We just store at 0 of r4, since we changed the value of it to point to the next element before we entered the loop. After we store the value, we add 4 to r4 so that it once again will point to the next element in the array. This will just set it up for the next iteration. The only thing left to do is loop back to the top. So it will decrement the CTR, and then branch as long as it’s not 0, which it isn’t. So it will just keep looping and looping through that code until it has filled the array with the first n numbers in the Fibonacci sequence. Here is a video of me stepping through the code.
You don’t have to use the count register if you want loop, however it is the easiest way. Instead, you could keep the counter in a GPR, and increment/decrement it each iteration. So we could do something like this if we wanted to.
li r3, 0
loopStart:
//some code to loop through
addi r3, r3, 1
cmpwi r3, 10
blt loopStart
So first we load r3 with 0. This means that instead of decrementing our counter each time, we are incrementing it. The we enter the loop, and it’ll execute whatever code you want it to run through. After it completes that, we increment our counter, and then we compare it with 10. We will loop back to the top as long as the counter is less than 10. This means that it’ll loop 10 times. You can loop through things this way if you prefer it, but using the CTR is easier in my opinion.