Creating Loops (Pt. 1)
#1
Creating Loops (Pt. 1)


Part 1: Simple Loop Creation



This thread will teach a coder how to write basic loops in Power PC ASM.

For this tutorial, we have a string of data starting at memory address 0x80002000.

The string of data is this..

Address    Data
80002000 11112222
80002004 33334444
80002008 44445555
8000200C 66667777
80002010 88889999

The following string of data is a total of 5 words in length (or 10 halfwords, or 20 bytes)

We want to copy this data to starting memory address 0x81405000

When dealing with data longer than 64 bits in length (two words), it's best to copy the data via a loop instead of using load-store instructions over and over again.

There are 3 types of loops:
The 'subic.' type loop
The 'bdnz' type loop
Call memcpy function loop (will not be covered in this thread, as it's vastly more complicated than the other 2 loops)

For both 'subic.' and 'bdnz' type loops, we will use load word update and store word update (can also be used for halfwords or bytes). If you are not familiar with this instructions, view the simple asm reference page HERE (scroll down to lwzu & stwu)


Subic. Loop



The 'subic.' loop is the easiest loop to create in my opinion. You use an available register as a 'counter/countdown' tracker to tell the loop how many times to repeat. Once this 'countdown' register hits zero, the loop will stop. The amount in the countdown register you use is dependent on how many words/halfwords/bytes you are loading/storing. For this example, we will stick with words. Since we have 5 words total, we will load the value of 5 into our countdown register. Let's pretend our countdown register is register 14. Thus, let's load 0x5 into r14

Code:
li r14, 0x5

Our countdown register is set. Obviously we need to setup the proper memory locations for where we are loading data from, and where we are storing that data too. Let's say we want to use register 15 as the address we want to being loading the string of data from.  So...

Code:
lis r15, 0x8000
ori r15, r15, 0x2000

And we're good. Or are we??? Actually, this is not correct. When you execute a lwzu, you need to have some sort of offset so the address can be incremented every time the lwzu is executed. Since we are dealing with words, its pretty obvious that offset will be 0x4. So every time 0x4 is added to the address, we are at the next word in memory to load data from. 

But this is an issue if r15 is 80002000 as the first time the lwzu is executed, we will load from address 800020004 (incorrect). Thus we need to initially set our loading addresss just -0x4 away from 80002000 which is 800019FC. Let's fix r15

Code:
lis r15, 0x8000
ori r15, r15, 0x19FC

OK, now we must apply that same logic to the address where we first store the data to.... We will use r16 for our address for storing.

Code:
lis r16, 0x8140
ori r16, r16, 0x4FFC

Notice that 81404FFC is -0x4 from 81405000.

Alright perfect, we have our beginning loading and storing addresses. Our countdown register is set. Now before we start the loop we need a register to hold the data temporarily when loading/storing. We will use r17 for that... Ok now let's get this loop started, here's how it would be configured.

Code:
loop_back
lwzu r17, 0x0004 (r15)
stwu r17, 0x0004 (r16)
subic. r14, r14, 1
bne+ loop_back

As you can see with the 'subic.' instruction, r14 is subtracted by 0x1 every time the loop is executed. The 'subic.' has a Record instruction in itself. This record instruction is shortcut for cmpwi r14, 0x0.

Obviously, if r14 is not equal to 0, we will jump back to the start of the loop. Once r14 equals 0, we know that all 5 words of data are loaded and stored. Thus the jump to loop_back will not occur and the code will continue on.

Let's put all our instructions together...

Code:
##some other ASM here##

li r14, 0x5

lis r15, 0x8000
ori r15, r15, 0x19FC

lis r16, 0x8140
ori r16, r16, 0x4FFC

loop_back:
lwzu r17, 0x0004 (r15)
stwu r17, 0x0004 (r16)
subic. r14, r14, 1
bne+ loop_back

##some other ASM here##

Alright, there you have it. A working model of the 'subic.' loop.


BDNZ Loop




The bdnz loop is a bit different. It requires the use of what is called the Count Register (CTR for short). The CTR is a register specifically designed to hold the countdown value of a loop. 

If we used this type of loop, we need to load our countdown value into r14 and then copy it to the CTR. Like this...

Code:
li r14, 0x5
mtctr r14

Alright, CTR has the value of 5. Of course, in this type of loop we don't need a 'subic.' instruction but the use of lwzu and stwu ASM instructions are still required. Instead of there being a 'bne' to use as a conditional jump, we will use 'bdnz' instead.

bdnz some_label_here = Branch Decrement When Not Zero; This literally means, decrement (subtract) the CTR by 1, then branch to the desired label, if and only if the CTR is not equal to zero. 

Example of our first loop converted to a bdnz type....

Code:
##some other ASM here##

li r14, 0x5
mtctr r14

lis r15, 0x8000
ori r15, r15, 0x19FC

lis r16, 0x8140
ori r16, r16, 0x4FFC

loop_back:
lwzu r17, 0x0004 (r15)
stwu r17, 0x0004 (r16)
bdnz+ loop_back

##some other ASM here##


Conclusion


And there ya go. A guide on how to create simple loops in PPC ASM. To dive into the realm of complex memcpy loops please go to Part 2 - HERE
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)