Assembly Tutorial
Assembly Tutorial

Chapter 1: Introduction

This tutorial will teach you how to read/write basic Power PC Assembly (computer programming language) for the purpose of making cheat codes for Wii games. This tutorial is a supplementation to my thread - 'How to Make your own Cheat Codes', which can be read HERE

The Power PC Assembly Language is the Assembly Language for the Wii's CPU. Broadway is the name of the CPU.

Chapter 2: Registers

What are Registers? They are a set of data holding places within the CPU. These data holding places are what Code Creators utilize to make their ASM Codes. There are all types of Registers. First thing's first. There are 32 normal integer registers. These registers are referred to as the General Purpose Registers (GPR for short).

There are also 32 Floating Point Registers (FPR for short). They obviously use floating point values instead of normal integer values. The Count Register (CTR) is used to help make loops and the Link Register (LR) holds the address that is used to navigate to/from a subroutine.

Most Wii codes only use the GPR's, thus these Registers are the only ones that will be discussed in further detail for this beginner's Assembly tutorial.

Data within Registers:

Each GPR holds a 32 bit length of data. For the Dolphin Emulator, every register is displayed in Hexadecimal form and every register has their entire length of data shown. Here is a picture of the GPR's with some values in them, taken when I did a random emulation pause of a Wii game.

[Image: GPRs.png]

32 bits of data is referred to as a 'word'. Since a register is 32-bits in length, the 'word' of a register is the entire register itself.

Using the picture above, let's take a look at the what's in Register 3 (r3);


16 bits of data is referred to as a 'halfword'. The 'upper 16 bits' of a register is the first half of the word value, while the 'lower 16 bits' is the second half of the word value.

802F = Upper 16 bits aka Upper Halfword
7560 = Lower 16 bits aka Lower Halfword

8 bits of data is referred to as a 'byte'.

80 = 1st Byte
2F = 2nd Byte
75 = 3rd Byte
60 = 4th Byte

Chapter 3: Compiler Basics

The Dolphin Emulator will emulate Broadway to execute the game's ASM instructions. An instruction will modify the register(s), the game's memory, or both. For your ASM cheat codes, you will have a compiler to write instructions in (The CodeWrite program mentioned in the 'How to Make your own Cheat Codes' thread).

Characters/symbol set:

When you write out ASM instructions in the compiler, various symbols are required for proper formatting. This will allow the compiler to interpret your ASM instructions and compile them correctly into a finished cheat code.

List of symbols:
. (period)
: (colon)
, (comma)
() (parenthesis)
+ (plus)
- (minus)
_ (underscore)
# (hash tag)
x (not multiply, this is for writing Hex values)

Hex vs Decimal:

Compiled cheat codes are shown in Hex byte code. It's common sense you need to know Hex beforehand. There are plenty of quick simple tutorials you can find via Google if you need to learn the basics of Hex. For writing ASM instructions, there are certain elements of an instruction that you can write in Hex. However, the downside is all known PowerPC compilers will decompile a cheat code using decimal representation. If you are not sure what to use, then I recommend using decimal for byte data and using Hex for all other data.

When you write Hex values in the compiler, you must pre-pend those values with '0x'. As an fyi, Dolphin displays all Register values in Hex but they are NOT pre-pended with '0x', as it's already assumed the user knows those values are displayed in Hex form.

Chapter 4: Format for Writing ASM Instructions

Any General Purpose Register is written as rX. X = the register's number. The register number is in decimal form. The first register is Register 0, aka r0. The last is Register 31, aka r31. Fyi: Dolphin may display r1 as sp, and r2 as rtoc.

In every instruction, there is a Destination Register. In most instructions, the Destination Register is the Register that holds the result of an executed instruction, while the Source Register is the Register that is used to compute the result for the Destination Register. Some instructions will have one source register, while others will have two. Every instruction can only have one Destination Register.

Here is a basic example of an instruction with two source registers~

rD, rA, rB

rD = Destination Register
rA = 1st Source Register
rB = 2nd Source Register

Keep in mind this is not an actual instruction, or an exact correct format. This is just to show you a very very general view of any instruction that uses two source registers to compute a value for the destination register. Now let's look at an example of an instruction with just one source register..


rD = Destination Register
rA = Source Register
VALUE = Immediate Value

Immediate Value is a decimal or hex value that CANNOT exceed 16 bits and it is NOT a register that contains a value. The use of Immediate Values allows Broadway to have instructions that can provide more flexibility with less register usage.

Before continuing further it's critical that you understand signed vs logical (unsigned) values.

What is signed & logical?
Values within a particular instruction (regardless of whether they are register values or immediate values) will either be signed or logical. Logical values are NEVER negative, while signed values CAN be negative.

The range of numbers in an entire register is 0x00000000 thru 0xFFFFFFFF. If an instruction uses signed values then some of these numbers are negative numbers.


Signed Range of Numbers in a GPR:

0x80000000 thru 0xFFFFFFFF = Negative Numbers.
0x00000001 thru 0x7FFFFFFF = Positive Numbers (if you don't include zero)

0xFFFFFFFF is -1 in decimal representation.
etc etc til you reach 0x80000000 which is the 'largest' negative number possible.

So a left to right visual would look like this...
0x80000000 --> 0x00000000 --> 0x7FFFFFFF

Logical (Unsigned) Range of Numbers in a GPR:

0x00000001 thru 0xFFFFFFFF = All Positive Numbers (if you don't include zero)


Since Immediate Values are 16-bits in size instead of 32-bits, their range of numbers will differ.

Immediate Value 16-bit Signed Range:
0xFFFF8000 thru 0xFFFFFFFF = Negative Immediate Values (-32768 thru -1)
0x0001 thru 0x7FFF = Positive Immediate Values; not including zero (1 thru 32767)

Left to Right visual:
0xFFFF8000 --> 0x0000 --> 0x7FFF

Immediate Value 16-Bit Logical (Unsigned) Range:
0x0001 thru 0xFFFF = All Positive Immediate Values; not including zero (1 thru 65535)


You will notice right away that negative Immediate Values are not 16-bit in size in the range shown above. This 'trick' allows Broadway to have negative 16-bit values displayed in a 32-bit register. When writing these Immediate Values in the compiler, you must follow the ranges shown above or else a compiling error will occur. Keep in mind you can write the Immediate Values in decimal form within the compiler if desired.

Chapter 5: Integer ASM Instructions

At this point you should have a well understanding of the...
Symbols that can be used in ASM instructions
General Format/Layout of ASM instructions

Let's go over actual real world instructions that a person would use to make codes. Here is one of the most basic ASM instructions....

Add (adds two source registers to compute the value of the destination register)

add rD, rA, rB

The value of rA is added with the value of rB. rD will hold the result of the two values added together. Values are treated as signed. Whatever value was in rD beforehand gets erased and replaced with the new value after the instruction has executed.

Let's say we add the values of r4, and r25. The result of this value will be stored in r20. Our 'add' instruction would be this...

add r20, r4, r25

For a majority of instructions that use two source registers, you can swap them. So you can also write this as...

add r20, r25, r4

Imagine this as a basic math equation of 2 + 3 = 5. It doesn't matter if you swap the positions of 2 and 3, the result is always 5. You obviously can't change the spot where the destination register is within the instruction. Keep in mind certain instructions won't allow the swapping of source registers.

Let's revisit the instruction of 'add r20, r4, r25'. Register 4 (r4) will be 3, and Register 25 (r25) will be 2. The picture below shows you an instance of this instruction right before it is executed. Both Source registers are circled in blue, and the Destination Register is circled in red. The add instruction is highlighted in green.

[Image: addBEFORE.png]

Do not concern yourself with the value of 1 in the Destination Register (r20) in the above picture. This is because once the CPU executes the add instruction, that value of '00000001' will be erased and replaced with the result of a r4+r25. Now view the following picture. It shows what happens once the add instruction gets executed. Take a look at the Destination Register circled in red.

[Image: addAFTER.png]

Once the add instruction was executed, r20 now has the value of 5.

Let's move onto another basic ASM instruction...

Add Immediate

addi r4, r30, 12

Notice the number 12. It doesn't have the letter 'r' before it. So we know 12 represents a 16 bit Immediate Value instead of a source register. Values in this instruction are signed. This instruction adds together the value of r30 and the value of 12. The result will be stored in r4. For the addi instruction, you CANNOT swap the positions of 12 and r30! If you wanted to write this same instruction in Hex form in the Compiler, it would be like this..

addi r4, r30, 0xC

The '0x' must be put before any hex value, or the compiler will compile it as decimal or not compile it at all (throw an error). You can of course throw a minus (-) before your signed value to designate a negative number. So if we did.....

addi r4, r30, -12

This would be adding the value of r30 and negative 12. Thus we are actually subtracting 12 from the value in r30. For simplicity, you can use what are called simplified mnemonics. A simplified mnemonic is a 'shortcut'/'simplified' version of an ASM instruction.

The simplified mnemonic for addi r4, r30, -12 is...

subi r4, r30, 12

Subi stands for Subtract Immediately. View the picture below. It shows you an instance of the 'subi r4, r30, 12' instruction right before it gets executed by the CPU. r30 (Source register) is circled in blue. r4 (Destination Register) is circled in red. The instruction itself is highlighted in green. Remember that there is no secondary Source Register, an Immediate Value is used instead.

[Image: subiBEFORE.png]

As stated earlier in the tutorial, registers are in hexidecimal representation. r30 being 0x0000000b is 11 in decimal representation. The subi instruction will preform 11 minus 12,  aka the value in the Source Register minus the Immediate Value of 12. Now view the picture below to see the result in the Destination Register (r4) once the subi instruction has been executed. Destination Register is circled in red.

[Image: subiAFTER.png]

r4 contains the result of 0xFFFFFFFF. Since the subi instruction treats its values as signed, this concludes that 0xFFFFFFFF is negative 1. Obviously that has to be the case since 11 - 12 = -1.

Let's now discuss the most commonly used simplified mnemonic of all...

Load Immediate

li r6, 0xFF

Values in this instruction are signed. As you can see there are no source registers in this simplified mnemonic. It is a shortcut for the add instruction for addi r6, 0, 0xFF. You will notice the 0 in the middle doesn't have an r in front of it...

Special note about r0:
In certain ASM instructions (such as addi), if r0 is used as the first source register, then it is treated by the compiler as literal 0.

Full list of special r0 rules:

The 'li' instruction simply sets a register to the designated immediate value. Which is 0xFF in our case. Therefore, after that instruction is executed, register 1 now has the value of 0x000000FF. 

Example of li to load a register with a negative 16-bit signed immediate value~
li r7, 0xFFFFFFFC

This will set r7 to 0xFFFFFFFC. You can also write this as...

li r7, -4

Add Instruction using a register for both a Source Register and the Destination Register:

add r4, r4, r30

In the above instruction, the value of r4 (before execution of the instruction) plus the value of r30 will then be placed in the value of r4 once the instruction has executed. Thus after the instruction has executed whatever old value was in r4 is now replaced by the new value.

Discussing CPU execution order and writing multiple instructions in the compiler.

Great you understand what occurs on a basic math-based instruction. But its important to understand how the CPU executes multiple instructions. The picture below explains this and provides some sample ASM instructions in a basic diagram.

[Image: cpuexec.png]

Writing multiple instructions in the compiler is what you would expect...

add r4, r4, r30
li r31, 1
addi r12, r31, 0xA

Each instruction takes up one 'line/row' in the compiler. You cannot put multiple instructions on one line. Once you have typed out an instruction, you must enter into a new line to write your next instruction.

Chapter 6: Store, Load ASM Instructions

This chapter will demonstrate how to take register values and write them to memory, and how to take values from memory and write them to the registers. Let's take a look at one of the most basic store-type (write a register's value to memory) instructions...

Store Word

stw rD, VALUE (rA)

Note: Values in this instruction are signed.

This instruction will copy the word (entire value) of rD and write it to a memory location that is referenced by the value in rA + VALUE. VALUE is a 16 bit signed hex/decimal number. With any store instruction, both rD & rA will not lose their data.

stw r3, 0x0020 (r28)

The word of r3 will be stored at the memory location (address) that is the value in r28 + 0x0020. The 0x0020 value is usually referred to as the term 'offset'.

Please also note that the memory location of rA + VALUE is usually referred to as the Effective Address.

Let's say our value in r3 is 0x0000200A, and r28 is 0x80001500. Add the offset value to 0x80001500.

0x00000020 + 0x80001500 = 0x80001520.

View the picture below to see an instance of this particular instruction right before it gets executed. The Destination Register (what will get written to memory) is circled in blue. The spot in memory where the write will occur at is circled in red. Source Register is circled in magenta. The instructioin itself is highlight in green.

[Image: storeBEFORE.png]

Now view the next picture to see what happens once the stw instruction is executed.

[Image: storeAFTER.png]

The blue arrow shows that the value in the Destination Register (r3) has been copied and pasted to the memory address of 0x80001520.

There are also sth (Store Halfword) and stb (Store Byte) instructions. The sth instruction will only store the lower 16 bits of a register to memory, while the stb instruction will only store the 4th byte (far right) byte of a register to memory.

Load Word & Zero

lwz rD, VALUE (rA)

Note: Values in this instruction are signed.

This is simply the 'reverse' of stw. The word at memory location rA+VALUE will be copied into rD. Whatever was in rD beforehand is now completely erased.

lwz r31, 0 (r15)

For this lwz instruction, the offset is 0 (no offset). Therefore, nothing (zero) is added to r15 so the effective address is simply r15's value. View the picture below to see an instance of this particular instruction right before it gets executed. Source register is circled in blue. The word value that will be copied from memory and written to the Destination Register is circled in red. The Destinationon Register itself is circled in magenta.

[Image: loadBEFORE.png]

The value of 7FE5FB78 is what will be written to r31. Now here's a picture of once the instruction has been executed.

[Image: loadAFTER.png]

The red arrow shows you that the word value in memory gets copied then pasted into r31.

There are also lhz (Load Halfword & Zero) and lbz (Load Byte & Zero) instructions. The lhz instruction loads a halfword from memory into a register. Whatever value was in the register beforehand gets erased. This means every time a lhz instruction gets executed, the rD for the instruction will always result with a value of 0x0000XXXX (XXXX being the halfword value that was loaded from memory).

The lbz instruction loads a byte from memory into a register. Whatever value was in the register beforehand gets erased. Thus, every time a lbz instruction gets executed, the rD for the instruction will always result with a value of 0x000000XX (XX being the byte value that was loaded from memory)

Chapter 7: Writing an Entire Word Value to a Register from Scratch

You are probably wondering at this point how to write a whole word value from scratch to a Register. This is useful for establishing memory locations to later use for store-type and load-type ASM instructions. So let's say we want to write the value of 0x80E6FF30 to Register 22, how do we do this? Simple, with just two ASM instructions like this...

First we write the upper 16 bits. For example:

Load Immediate Shifted

lis r22, 0x80E6

NOTE: Values in this instruction are Signed.

Lis is an odd instruction. You will notice that even though the lis instruction treats its values as signed, the example above is using a 16-bit Immediate Value in the Logical Range. Without needing to get into endless technical detail which as a beginner you won't understand, this is basically due to how all PPC ASM Compilers are setup. Don't worry as this 'breaking of the rules' only applies to the lis and addis instructions (lis is a simplified mnemonic of addis).

So just to recap, whenever you are writing lis instructions in your compiler. You can use logical number ranges, but remember lis treats its values as signed.

Load Immediate Shifted (lis) is similar to the Load Immediate (li) instruction but you are setting the upper 16 bits of a register instead of the lower 16 bits. Whenever any lis instruction is executed the lower 16 bits are always CLEARED (set to 0000)!

So at this point, r22 has a value of 0x80E60000. To write in the lower 16 bits without effecting the upper 16 bits, we do this with an instruction called Or Immediate (ori). Before explaining the ori instruction, here's a picture of the above lis instruction (lis r22, 0x80E6) being executed by the CPU (you will see an ori instruction highlighted in green but it has NOT YET executed).

[Image: lisori1.png]

As you can see in r22 (circled in blue), the lower 16 bits have been cleared and it now contains the value of 0x80E60000. Let's now discuss the ori instruction...

Or Immediate

ori r22, r22, 0xFF30

NOTE: Values in this instruction are Logical

When writing out an ori instruction for the purpose covered in the tutorial, be sure the ori instruction's Destination and Source Register is the same register that was used in the lis instruction. If you are wondering what exactly happens with the Or Immediate instruction and you are not familiar with Logical Operations (And, Or, Xor), I wouldn't concern yourself with it for now. Just remember to use lis and ori instructions if you need to set an entire word value into a register from scratch.

Here's a picture of the above ori instruction (ori r22, r22, 0xFF30) executed by the CPU.

[Image: lisori2.png]

As you can plainly see, r22 now has the full value of 0x80E6FF30. In conclusion, if you were to write the lis and ori instructions in the compiler, it would look like this...

lis r22, 0x80E6
ori r22, r22, 0xFF30

Chapter 8: Branch, Compare ASM Instructions

Branch instructions are used as 'jumps' to skip over certain other instructions. Let's take a look at the most simple branch instruction...


b 0x8
li r3, 1
stw r3, 0 (r31)

The letter b is used for what is known as an unconditional branch. Unconditional meaning the branch is executed no matter what the conditions are. Think of it like a jump. The branch will skip/jump over a certain amount of instructions below, thus not executing said instructions. In the provided example, the 'li r3, 1' instruction would be skipped. Now, the '0x8' next to branch is the amount to 'jump/skip'. This 'jumping' value is signed by the way, meaning you can have branches that jump backwards. Since each instruction is 4 bytes in compiled length, a jump of 0x4 would be pointless as this would simply just go down to the next instruction below. Obviously, the larger the jump, the harder it would be to correctly calculate the amount to write for the branch instruction. Therefore, we use a trick called 'labels'.

Labels are just that, they are labels.  Wink

To allow the compiler to know you are using labels, you designate labels with two symbols. The underscore symbol and the colon symbol. To first establish a branch label name, you must implement an underscore somewhere in the name. Like this...

b the_label

You can name labels whatever you want as long as you do NOT use special characters like percent signs or dollar signs. You can implement the underscore symbol if you want like the example provided. Okay, you have set the label name, now all you need to do is put that same label name right before the first instruction that you want executed after the jump has occurred. Put in the label name and add a colon afterwards like this...

b the_label
li r3, 1

stw r8, 0 (r31)

Here is a picture showing the direction of the CPU execution plus some more notes to give you a better 'visual' look.

[Image: simplebranch.png]

Now the branch instruction in the provided example above would be useless. Why would you randomly skip over ASM instructions? Well branches are needed if you wanted to create a subroutine. Think of your list instructions like a road. When the game is preforming the list of instructions one after another, think of that like traffic driving on the road. However, you can now put a fork in the road, and tell the traffic which way route to take. The two routes will then later merge back together.

Let's dive into Conditional Branches. We need a create that 'fork' in the road. The easiest method to create that fork is conditional branching. Conditional branches are branches that only execute base on an 'if'. For example let's look at the 'branch if not equal' instruction...

Branch If Not Equal

bne the_label

li r8, 1

stw r8, 0 (r31)

the_label will only be 'jumped to' if the conditional branch is true. In order to set up this 'if' for a conditional branch, we need to make a comparison. The most common instruction to establish a comparison is Compare Word Immediate.

Compare Word Immediate

cmpwi rD, VALUE

NOTE: Values in this instruction are signed

Value in rD is compared to VALUE as signed values.

cmpwi r10, 0xA

The signed value in r10 will be compared to the signed value of 0xA. We have thus created our 'if statement'. So now add in the rest of the instructions from earlier....

cmpwi r10, 0xA
bne the_label

li r8, 1

stw r8, 0 (r31)

The value in r10 is compared to the value of 0xA. Then, if the value in r10 is NOT equal to 0xA, you will 'jump' to the_label, thus skipping the 'li r8, 1' ASM instruction. Here's a picture giving you a better visual of what is occuring.

[Image: cbranch.png]

Now let's move onto a different conditional branch instruction...

Branch If Equal

cmpwi r10, 0xA
beq the_label

li r8, 1

b the_end

stw r8, 0 (r31)

stw r3, 0x0010 (r24)

As you can see not only are we using 'beq' now, we are adding an unconditional branch and a second label called the_end. You should quickly see why I added the unconditional branch. Remember the road analogy I used earlier... Let's follow the first route of the fork in the road (if r10 does equal 0xA)

If r10 equals 0xA, we jump to the_label. We then execute the first 'stw' instruction.... Now remember the traffic/road analogy, we now go right to the next ASM instruction below, the second 'stw' instruction. The label name itself is NOT a barrier in our 'road' in any way shape or form. The labels are just label names to calculate the branch offsets for the compiler so you don't have to do the work.

Now, let's instead take the second route of the fork in the road. If r10 is NOT equal to A, we do NOT jump to the_label. We instead go straight down our road to the 'li' instruction. After that, we encounter our unconditional branch. This obviously means we take the branch/jump no matter what. We do this because why would we go to the_label when our r10 value was NOT equal to 0xA? That would make no sense. Therefore, we jump to the_end, thus skipping the first 'stw' instruction.

Still confused? Here is a picture giving you a better visual.

[Image: cbranch2.png]

Here is a list of commonly used conditional branch instructions.
beq - Branch If Equal
bne - Branch If Not Equal
bgt - Branch If Greater Than
blt - Branch If Less Than
bge - Branch If Greater Than Or Equal To
ble - Branch If Less Than Or Equal To

Let's go over another compare instruction really quick... 

Compare Word

cmpw rD, rA

NOTE: Values in this instruction are signed.

This will simply compare the signed values of two registers.

cmpw r4, r8
bgt the_label

In this example, if the value in r4 is greater than the value in r8, then the jump to the_label will be taken.

Chapter 9: Overall Illustration

Here's a picture I made to give you a general visual guide of what ASM instructions do..

[Image: ASMpic.png]

Math-based, comparison, and branch instructions only modify the registers. If you need to have the registers effect what's in memory and vice versa, you would use load and/or store instructions.

Instructions executed by the CPU are in Static Memory. While the store and load instructions being executed will effect data that resides in Dynamic Memory.

Chapter 10: Extra Stuff

Let's go over some more symbols that we haven't covered yet.

Period (.):

You can use the period to establish a value to have it's own unique label name. Btw, this has nothing to do with branch labels. Think of these like making definitions, or having 'macros'. The period is followed by the word 'set'. For example:


...some ASM here....


This now allows the ASM writer to put ITEM_MUSHROOM for any time we wants to use the value of 0x4. Very basic 'macro' per say. Can come in handy if you are writing lengthy ASM.

Plus & Minus (+ and -):

The plus and minus symbols are used for conditional branches. Whenever a branch is done, you can help Broadway by supplying a 'hint'. The plus symbol stands for more-likely, while the minus symbol stands for less-likely. For example....

cmpwi r8, 0xC
bne+ the_label

The plus symbol next to the 'bne' will tell Broadway that the branch is more-likely to occur.

Hash Tag (#):

Whenever someone is writing very lengthy ASM, it can be handy to add notes that will let that someone know why he/she wrote those instructions. Here's an example of using hash tags to add notes/comments:

#Start assembly source

lis r4, 0x8000 #Set 1st half address to the store word to
stw r30, 0x157C (r4) #Store word to memory location 0x8000157C, the offset amount is used to complete 2nd half of address

#End assembly source

Chapter 11: Conclusion & Credits

Alright, this should help get you started writing PowerPC ASM for your cheat codes. For more instruction examples, visit this thread HERE.

IBM, Apple, and Motorola (creators of PowerPC ASM)
WiiBrew (a lot of information was gathered from there)
Star (taught me ASM)

Messages In This Thread
Assembly Tutorial - by Vega - 11-14-2018, 02:19 PM
RE: Assembly Tutorial - by xXCrazySniperXx - 11-14-2018, 10:30 PM
RE: Assembly Tutorial - by xXCrazySniperXx - 11-27-2018, 02:28 AM
RE: Assembly Tutorial - by Vega - 11-27-2018, 03:11 PM
RE: Assembly Tutorial - by KartPlayer - 06-18-2019, 12:52 PM
RE: Assembly Tutorial - by Vega - 06-18-2019, 06:18 PM
RE: Assembly Tutorial - by KartPlayer - 06-19-2019, 08:34 AM
RE: Assembly Tutorial - by Vega - 06-19-2019, 10:14 PM

Forum Jump:

Users browsing this thread: 1 Guest(s)