Conditional Branching

The ability to control program flow is one of the most fundamental and important aspects of assembly language programming. Without it, microcontrollers would just sequentially execute the instructions in their memory until they ran out. Writing a non-trivial program this way would be extremely difficult if not impossible.

Controlling program flow means we instruct the microcontroller to jump from one address in code to another - referred to as branching. This can be done with three distinct methods:

  • Conditional Branching
  • Jumps
  • Subroutine Calling

Conditional Branching allows you to test a condition (e.g. whether a bit is set or a register equals a particular value) and branch to a new location if the condition is true. If it is not, the microcontroller will continue to the next instruction.

Jumping is a form of Unconditional Braching. It redirects program flow regardless of conditions. Jumping allows you to simply jump to a new address in program memory.

Subroutine Calling is another form of Unconditional Braching. It works similarly to Jumping in that it causes the microcontroller to jump to a new address in program memory. However, the microcontroller will remember the address the subroutine call was made from, and upon reaching the end of the subroutine, return to the instruction immediately following the subroutine call.

In this tutorial, we will look specifically at Conditional Branching, so named as it creates multiple paths through a program.

Setting Flags in the Status Register

Most of the Conditional Branching instructions operate based on flags in the Status Register. As we saw previously, flags are automatically set in the Status Register after performing any operation. Thus, Conditional Branching instructions can often be placed organically in a program without other supporting instructions.

However, sometimes a condition needs to be tested on a register that was not affected by the previous operation. In this case, we have a few instructions at our disposal, shown below.

Mnemonic Description
cp compare
cpc compare with carry
cpi compare with immediate
tst test for zero or minus

The cp instruction allows us to compare two registers.

	ldi	r16,0x01		; load r16 with 0x01
	ldi	r17,0x02		; load r17 with 0x02
	cp	r16,r17			; compare r16 and r17

What this instruction really does is subtract the second register from the first to set flags in the Status Register. However, cp leaves the registers themselves unmodified. Using the flags in the Status Register we can determine which register is larger or if they have the same value.

In the example above, the Carry Flag will be set in the Status Register after the compare, since we are essentially computing 0x01 - 0x02. Because of this we can conclude that the second operand is larger than the first.

Alternatively, in the following we check if two registers are equal in value.

	ldi	r16,0x01		; load r16 with 0x01
	ldi	r17,0x01		; load r17 with 0x01
	cp	r16,r17			; compare r16 and r17

After the compare, the Zero Flag will be set since 0x01 - 0x01 = 0x00. Thus, we can conclude the two operands are equal.

If more than one byte must be compared, the cpc instruction can be used which will include a carry bit in the comparison. For example, to compare the 16-bit numbers 0xAA01 and 0xAA02

	ldi	r16,0x01		; load r16 with 0x01
	ldi	r17,0xAA		; load r17 with 0xAA
	ldi	r18,0x02		; load r18 with 0x02
	ldi	r19,0xAA		; load r19 with 0xAA

	cp	r16,r18			; compare r16 and r18
	cpc	r17,r19			; compare r19 and r17 with carry

After the first compare, the Carry Flag will be set, indicating that the first number is less than the second.

In the second compare, even though the high bytes of both numbers are equal, the Carry Flag will still be set, correctly indicating 0xAA02 is greater than 0xAA01. If the cp instruction had been used instead, the Status Register would incorrectly indicate the two numbers are equal.

For registers 16 through 31, we have an immediate comparison, cpi.

	ldi	r16,0x05		; load r16 with 0x05
	cpi	r16,0x05		; compare r16 with 0x05

In the above example the Zero Flag will be set, indicating r16 is equal to the constant we are comparing it against.

Lastly, the tst instruction can be used on a single register to check if it is negative or zero. tst will set both the Sign and Negative Flag if bit 7 of the register is set, or set the Zero Flag if the register is zero. tst will always clear the Two's Complement Overflow Flag.

	ldi	r16,0x80		; load r16 with 0x80
	tst	r16			; test r16

In the above, the Sign and Negative Flag will be set since bit 7 of 0x80 is 1.

Conditional Branching

Once flags are set in the Status Register, there are a variety of Conditional Branching instructions that can control program flow.

Mnemonic Description
brbs branch if status flag set
brbc branch if status flag cleared
breq branch if equal
brne branch if not equal
brcs branch if carry set
brcc branch if carry cleared
brsh branch if same or higher
brlo branch if lower
brmi branch if minus
brpl branch if plus
brge branch if greater or equal, signed
brlt branch if less than, signed
brhs branch if half carry set
brhc branch if half carry cleared
brts branch if t flag set
brts branch if t flag cleared
brvs branch if overflow flag set
brvc branch if overflow flag cleared
brie branch if interrupt enabled
brid branch if interrupt disabled

The syntax for a branch instruction is simply

	branch	label

If the condition specified by the branch instruction is satisfied, it will jump to the address marked by the label. Otherwise, it will continue to the next instruction.

For example, the following code will compare r16 and r17 and branch to lbl if they are equal.

	cp	r16,r17			; compare r16 and r17
	...
	breq	lbl			; branch if r16 == r17
	...
lbl:

Some of the Conditional Branching instructions may seem redundant, but that is only because there is an unsigned and signed version. For example, brsh - branch if same or higher, is used with unsigned values

	ldi	r16,0x9C		; load r16 with 0x9C (156 unsigned)
	ldi	r17,0x0F		; load r17 with 0x0F (15 unsigned)
	cp	r16,r17			; compare r16 and r17
	...
	brsh	lbl			; r16 is >= r17, code will branch
	...
lbl:

The instruction brge - branch if greater or equal, performs the same type of comparison but works with signed values.

	ldi	r16,0x9C		; load r16 with 0x9C (-100 signed)
	ldi	r17,0x0F		; load r17 with 0x0F (15 signed)
	cp	r16,r17			; compare r16 and r17
	...
	brsh	lbl			; r16 is not >= r17, code will not branch
	...
lbl:

You may notice there are instructions for checking if a register is greater than or equal to another register, and corresponding instructions to check if a register is less than another register. However, there are no less than or equal comparisons. This is because they are not needed. Less than or equal can be checked by simply swapping the order of the operands with a greater than or equal instruction. For example

	ldi	r16,0x9C		; load r16 with 0x9C (156 unsigned)
	ldi	r17,0x0F		; load r17 with 0x0F (15 unsigned)
	cp	r17,r16			; compare r17 and r16
	...
	brsh	lbl			; r16 is not <= r17, code will not branch
	...
lbl:

I won't belabor the usage of the above instructions as their descriptions make it fairly obvious. Instead, we will look at how to create familiar structures from high level programming languages using Conditional Branches.

While Loop

A While Loop in assembly can be constructed as follows

loop:	cpi	r16,100			; compare r16 and 100
	...
	brsh	next			; branch if r16 >= 100
	rjmp	loop			; repeat loop

next:

In the above, the loop will check if r16 is less than 100 at the beginning of each iteration. If it is not, it will execute whatever code is in the loop, then jump back to the beginning and compare again. If r16 is greater than or equal to 100, the loop will break and the program will jump to the next label.

This is equivalent to the following C code, assuming r16 is stored in the variable x.

while(x < 100)
{
   ...
}

Do While Loop

Alternatively, a Do While Loop can be constructed in assembly as

loop:	...
	cpi	r16,0			; check if r16 is zero
	brne	loop			; repeat loop if r16 is not 0

Note that in a Do While Loop we do not need an rjmp instruction. The Conditional Branch is used to repeat the loop, and the program simply continues to the next instruction when the condition is not met.

This is equivalent to the following C code, assuming r16 is stored in the variable x.

do
{
   ...
}
while(x != 0)

For Loop

An assembly For Loop is shown below

	clr	r16			; clear r16
loop:	cpi	r16,11			; compare r16 to 11
	breq	next			; break loop if equal
	...
	inc	r16			; increment r16
	rjmp	loop			; repeat loop

next:

In this example, our For Loop index is stored in r16 and initialized to 0. r16 is compared to a count of 11 and will break out of the loop when it is equal. Note that this is our way of checking if r16 is less than or equal to 10.

This is equivalent to the following C code, assuming r16 is stored in the variable x.

for(i = 0; i <= 10; i++)
{
   ...
}

"Skip" Branching

In addition to the Conditional Branching instructions above which will branch to a label when a condition is true, there are also a few instructions that will simply "skip" an instruction if a condition is set. They are shown in the table below.

Mnemonic Description
sbrc skip if bit in register cleared
sbrs skip if bit in register set
sbic skip if bit in i/o register cleared
sbis skip if bit in i/o register set

The sbrc and sbrs allow you to "skip" the following instruction if a bit in a general purpose working register is set or cleared. For example, the following will create a loop that will run until bit 3 of r0 is set.

loop:	...				; loop code
	sbrs	r0,3			; break loop if bit 3 of r0 is 0
	rjmp	loop			; repeat loop

Additionally, sbic and sbis can be used in the same way for I/O Regisers. For example, the following will create a loop that will run until PINB3 is cleared.

loop:	...				; loop code
	sbic	PINB,3			; break loop when PINB3 is 0
	rjmp	loop			; repeat loop

Conclusion

There you have it, Conditional Branching in AVR Assembly. Understanding the above is a significant step forward in writing useful assembly language programs.

rjhcoding.com 2018