A switch/case Implementation in Assembly

Last modified by Microchip on 2024/02/06 09:30

Often, when programming in assembly, we run across situations where we'd like to implement a programming construct from a high-level language like C. C's SWITCH statement finds many uses in embedded applications, but no such construct exists natively in the assembly language. However, it is fairly easy to recreate the functionality using the microcontroller's fundamental operations along with an infrequently used macro feature of the assembler and some good old Boolean algebra trickery that many of us have long since forgotten.

Boolean Algebra Refresher: The XOR Operator

In order to create the switch/case construct in assembly, we need to review some of the fundamental properties of the XOR operator.

Truth Table


Back to top

Algebraic Properties

CommutativeA ⊕ B = B ⊕ AOperand order doesn't change the result.
Associative(A ⊕ B) ⊕ C = A ⊕ (B ⊕ C)Operand grouping doesn't change the result.
Non-IndempotencyA ⊕ A = 0XORing an operand with itself is zero.
IdentityA ⊕ 0 = AXORing an operand with zero doesn't change the operand.

These properties are used in combination with each other to perform a little trick of logic that will be outlined in the "Step-by-Step Analysis" section. Without a working knowledge of these properties, the code won't make much sense.

Back to top

The Code

In this code snippet, SWITCH is a register (variable) that contains the value we are trying to match to each of the cases. The labels CASE1CASE2, and CASE3 are constants and are the values we are checking against the one stored in SWITCH. The labels LABEL1LABEL2, and LABEL3 are the names of subroutines we want to jump to for each match condition.

movf      SWITCH, w
xorlw     CASE1
btfsc     STATUS, Z                ; If SWITCH = CASE1, jump to LABEL1
goto      LABEL1
xorlw     CASE2^CASE1
btfsc     STATUS, Z                ; If SWITCH = CASE2, jump to LABEL2
goto      LABEL2
xorlw     CASE3^CASE2
btfsc     STATUS, Z                ; If SWITCH = CASE3, jump to LABEL3
goto      LABEL3

This code snippet takes advantage of the properties of the XOR operator and the assembler's ability to perform calculations on constants at build time (more in the "Step-by-Step Analysis" section). There are several variations floating around the net, so this is certainly not the only way to implement a switch-like construct in assembly. The previous code snippet should work on any 8-bit PIC® microcontroller. With minor modifications to the syntax, it should also work on a 16-bit PIC microcontroller and dsPIC® Digital Signal Controllers.

Back to top

Step-by-Step Analysis

movwf SWITCH, w

This simply moves the value from the register labeled SWITCH into the W register. Mathematically, we can write this as:

xorlw CASE1

This performs a bitwise exclusive OR operation (at runtime) between the W register and the constant CASE1. The result is stored in W. Mathematically, this can be written as:
W = W ⊕ CASE1

Substituting for the original value of W established on line 1:

btfsc STATUS,Z

This tests the outcome of the previous operation. If the result of SWITCH ⊕ CASE1 is zero, then the Z bit in the STATUS register will be set. So, what this line is saying is that if the Z bit is clear (the previous operation did not result in a zero), then skip the next instruction. The reason we are performing this test is based on the property of non-idempotency: A ⊕ A = 0. So, if the value in SWITCH is the same value as CASE1, then SWITCH ⊕ CASE1 = 0. If that is the case, we found our match and want to execute the next instruction.

goto LABEL1

This line takes us to a subroutine with the name LABEL1 to handle the situation when SWITCH ⊕ CASE1 = 0. If the Z bit in the STATUS register was not set above, then this instruction would be skipped and we would test the next condition.

xorlw CASE1^CASE2

This line is where most of the magic occurs. We are playing several tricks at once. The first thing to point out is that the ^ symbol is the XOR operator in the assembler's macro language. Macro operators perform calculations on the computer at build time and are never executed on the PIC microcontroller. Therefore, this operator may be used only with constants or other values that are known at build time. In this example, CASE1, CASE2, and CASE3 are all constants defined in our code and therefore known at build time. So, this line of code will XOR the value in the W register with the calculated value CASE1^CASE2. Mathematically, this can be written as:
W = W ⊕ (CASE1 ⊕ CASE2)

However, W contains the result of the previous operation SWITCH ⊕ CASE1. Substituting for the previous value of W:
W = (SWITCH ⊕ CASE1) ⊕ (CASE1 ⊕ CASE2)

At this point, we can take advantage of some of XOR's properties. First, we use the associative and commutative properties to rewrite the equation:
W = (SWITCH ⊕ CASE2) ⊕ (CASE1 ⊕ CASE1)

Next, we use the property of non-idempotency (A ⊕ A = 0):
W = (SWITCH ⊕ CASE2) ⊕ 0

And finally, we use the identity property (A ⊕ 0 = A):

This is exactly what we want to test to see if SWITCH = CASE2! Now, this is just like what we did on line 2. From this point forward, the code just repeats itself with different values.

The code may be repeated for as many CASEn values you wish to use.

Back to top