Flag Manipulation in the Microcontroller (Prelude to discussion on branching)

Tuesday, October 29, 2013

As an instructor, one of the things I have seen create more headaches than I care to admit is the topic of microcontrollers. For some reason, the concept of assembly language, albeit a reasonably unsophisticated language, is something that eludes and baffles a great deal.

This article is the first in a series aimed at clearing some of the mysteries of the microcontroller. In our first phase, the mysteries we shall examine are flag manipulation and program execution. The way we shall approach the problem is through a small program and the observance of what transpires during each and every step. Once we are done the concepts of program execution and flag conditions should hopefully make more sense.

We shall then continue the discussion of flags and how they affect the branching instructions in the next several articles.

Since many of you are or will be familiar with the 68HC11, we are going to use the instruction set (more specifically the op codes) to illustrate our path. There is also a bit of presumption that a slight understanding of conditional changes to them flags themselves is already in your “bag of tricks.”

As you already know, part of our use of the microcontroller is dependent upon conditions that occur to the flags as the program is executed, and in return the conditional branch instructions give the microprocessor the power to make decisions. As the name implies, a certain condition must be met before a branch takes place. The flags monitor the A Register, and signal the presence of a specific condition. If the microprocessor encounters a conditional branch instruction, it checks the flags to see if the condition is satisfied. If the flag status meets the condition of the test, the program branches off to another section; if not, the normal program continues.

Therefore, the conditional branch instructions inherit their power from these simple condition code registers. A sound knowledge of how these work is important to your understanding of the microcontroller system. Notice that there are eight flags present on the 68HC11, but we are only concerned with four of them. In this first article, you will investigate a program that is designed to manipulate the N, Z, V, and C flags (also known as the sign flag, the zero flag, the overflow flag, and the carry flag). The program we shall examine for this is shown below.

Mnemonic

Comments

NOP

No operation.

ORAA #$FF

OR Reg. A immediately with FF16.

ANDA #$77

AND Reg. A immediately with 7716.

ANDA #$00

AND Reg. A immediately with 0016.

ORAA #$01

OR Reg. A immediately with 0116.

LDAA #$92

Load Reg. A immediately with 9216.

ADDA #$C6

Add immediately C616.

LDAA #$08

Load Reg. A immediately with 0816.

ADDA #$08

Add immediately 0816.

LDAA#$01

Load Reg. A immediately with 0116.

SUBA #$02

Subtract immediately 0216.

LDAA #$77

Load Reg. A immediately with 7716.

SUBA #$66

Subtract immediately 6616.

LDAA #$49

Load Reg. A immediately with 4916.

ADDA #$60

Add immediately 6016.

LDAA #$10

Load Reg. A immediately with 1016.

STOP

Stop.

Look at the first instruction in the program; the mnemonic is "NOP." There is a reason for placing this here. What you need to realize is that if you entered this into the microcontroller and ran it, you probably would notice that when you single‑stepped through a program, you would never see the first instruction. This is due to the processor executing the command as it "walks" past the instruction, which means we will only see the next command, and not the first.

To correct this problem, we would insert the NOP. The micro would "see" this as being the first command, even though there is nothing being accomplished. Therefore, the machine displays the next instruction, which is the first "real" instruction of the program, thereby allowing you to view it before it is actually performed. It is one of the ways a programmer can ensure that what needs to be done is being done, and it really only adds one line of code and 2 machine cycles (a minimal amount of extra time to avoid grief later if the program is really large).

The other thing we need to understand is the NOP does nothing to any of the registers or flags, so whatever is there to start remains there after that command has been executed. Therefore, we shall start looking at the flags and the A Register in more detail with what would be the second command, the ORAA #$FF line.

When we execute this instruction, it will immediately OR whatever is in the A Register with FF16. Since the OR function means that a high will be the end result whenever a high is encountered in the A Register or the operand, we can guarantee that the end result in the A Register will be FF16 (11111111 + 00000000 = 11111111). Since all of the bits are high in the A Register, it automatically means the Zero and the Carry will be reset (O), but the Sign flag will be set (1). This is due to the monitoring of the MSB (Most Significant Bit); whenever it is high, the N flag will be set, and we use the Overflow to further verify if the number is indeed negative. If it is, then probably the number was meant to be negative; if not, then we are dealing with a positive result that just appears to be negative. In this case, since the Overflow flag should be always be reset on both the logical operations (AND as well as OR), we presume the latter statement.

The next instruction executed is the ANDA #$77. The AND operation means that the bit position in Register A and the corresponding bit position in the operand must be high for a high to appear in the result. In this instance, we have 11111111 in Register A, and 01110111 in the operand. Notice that only bit 3 and bit 7 of the operand is low, so the end result in Register A after the operation can only be 7716 (01110111). This makes the MSB low, so now we have all of the flags in a reset condition (N = 0, Z = 0, V = 0, C = 0). In essence, the only reason we changed the N flag was due to the fact that the only monitored bit that changed was the MSB; all other flags remained unaffected. Onward to the next step!

Our next step would execute the ANDA #$00. This means the same type of instruction as the previous step will be performed, but this time we are using the operand of 0016 (00000000). Understanding that both the register’s bit AND the operand’s corresponding bit have to be high to get a high out, we can automatically predict the final outcome is going to be a zero result in Register A (0016) since none of the bits in the operand are high to begin with. Now we can examine the flags and still state confidently that the Carry, the Overflow and the Sign flags will be reset, but the state of the Zero flag will be a high (set or 1 if you prefer). This condition happens only once in a great while, and it happens to a very specific condition. Whatever register we happen to be monitoring had better contain the number 0016 in it for this flag to pay attention, as a high in at least one bit position means that all bets are off and the flag will be reset. Guess that kind of gives you the idea of how the next OR operation is going to affect the flags, as the only one we need to watch is the Z flag. ORing the 0016 with 0116 (ORAA #$01) gives us 0116 in the A Register, so the Z flag says goodnight and takes a well-deserved rest by resetting itself. All other flags did not need to watch, which means everybody starts with a 0 in their corresponding corners. Now we get to the good stuff…

Looking at the program, you can see that we are pretty much done playing with the logical functions and getting into the meat of the mathematical operations. Granted, it is nothing more than addition and subtraction, but truth to be told multiplication is nothing more than repeated addition, and likewise division is nothing more than repeated subtraction. So we are going to take these thoughts and throw some numbers around while we watch the flags try to keep up with us and our numerical manipulations.

The first actual mathematical operation we are going is add, but in order to get there we want to load the A Register with a number. You might want to ask why, but then looking at the sequence you might see there is a method to my madness (Or is that a madness to my method? Kind of get those two backwards for some reason….). I wanted to get all of the flags in the reset condition, and all of the preceding steps seemed like the most efficient way to do the deed. Besides, it was kind of fun.

When we execute the command to load the A Register with 9216, all previous information just goes away. We have no record of it, and the microcontroller forgets it existed. It just looks at the new number, and based upon the bit pattern makes some of the flags change states. Which flags you might ask? Only those flags that do not depend upon some sort of math or logic function being performed, which in our case would only be the Z flag (Zero) and the N flag (Sign). The only problem with that thinking is what happens to the Overflow flag. It will become reset with the load operation, as again we need to refer back to the concepts introduced towards the beginning of how the V flag is used. The number needs to be manipulated, not just entered for it to change states and show us if the number is truly positive or negative. To make sure we are all on the same page, the flags will be in this condition once the load command has been executed: N = 1, Z = 0, V = 0, C = 0. We move on now to the ADDA #$62 command.

This command, once executed will add the contents of the A Register (9216) with the operand of 6216. The math being what it is will return the value of 5816 in the A Register, which of course resets the N flag. However, we now see that the C flag would become set, as the end result is beyond the confines of the 8 bit register. The actual value of the result is 15816, and the C flag helps to illustrate the fact that the addition of the MSB in both numbers generated a value beyond the register’s ability to contain, which did cause the Carry bit (C flag) to show set. This of course also resets the N flag, as the MSB in the A Register is not high, and of course there being a multitude of 1s in the number means the Z flag would also have to be reset. Since the N flag is reset, there is no need to monitor the V flag to see if it really is supposed to be a signed or unsigned number (meaning negative instead of positive as all unsigned numbers are always presumed positive). This makes the entire sequence of flags to be N = 0, Z = 0, V = 0, and C = 1. Now we move to the next load instruction (LDAA #$08).

This instruction is not meant to change flags (specifically, we do not want to change the C flag), so the state of all of the flags will be the same as the previous step (N = 0, Z = 0, V = 0, C = 1). When we load 0816 into the A Register, all it does is replace the contents and show us 0816 instead of the 5816 it previously contained. If the V flag had been set before this step, again remember it would have been reset by the command. Now we need to step through the ADDA #$08. It is going to add the two numbers, and give us the value of 1016 (00010000). This will cause only the C flag to change states, as there is no carry from the MSB; it also means the other flags remain in their previous states as there is still one high in the value which means it cannot be considered zero, and the MSB never went high so it cannot be negative either. This leaves us with the following conditions for the flags: N = 0, Z = 0, V = 0, C = 0. Next, we will examine the load instruction LDAA #$01.

Once again, we are replacing the contents of the A Register, this time with 0116. As before, there should be no effect on any flags by the load as the N and the Z are already reset, and the V flag would become reset by the command if it were not so already. The C flag is not affected by the load, so this is why we again say it is just the same as before the command. Now when we subtract 0216 from the A Register, this will cause the value to become -1 in decimal, which is FF16 for us in hexadecimal. The N flag will be set, showing the negative number, the Z flag will remain reset, while the V flag will become set to indicate the negative number. The one flag we have not mentioned yet is the Carry, but in this case it is no longer indicating a carry from the addition, but instead will show a set condition from the fact that when the number in the register is smaller than the operand, and we subtract, it has to be done with a borrow in mind. So this will in fact set the C flag, as now it indicates there was a borrow operation from the flag to the MSB for the subtraction instead of the carry from the MSB indicating addition.

For the next step, we will allow the program to continue to the LDAA #$77. This command will only change the N flag, as the number 7716 as you may recall was 01110111 when considered in its binary form. This shows us that the MSB is not set, so there is no need for the N flag to indicate a negative number. Remember as well that the C flag retains its previous state until we do some sort of numerical manipulation, so the SUBA #$66 is going to be important here, especially since it is the next step.

SUBA #$66 will subtract the 6616 from the number 7716 that was previously loaded into the A Register, leaving us with 1116 in the A Register. This will cause the C flag to reset since there was no carry from the operation, and all of the other flags will stay in their reset modes as well as the number is not zero, it is not negative and therefore does not need the V flag to show that it is signed. Only a few more steps to go…

Our last mathematical operation will first start with loading the A Register with 4916 (a positive number), and then adding to it the value of 6016 (another positive number). The result will be A916, which could be interpreted as a negative number since the MSB is high. That means that we will have to look beyond just the N flag to the V flag for the state of affairs there. Since the V flag should not be set, we can safely say the number is still a positive. The last command before the STOP is the LDAA #$10, which will reset the N flag and leave all of the others reset. The STOP command only causes the clocks to stop moving us forward, which in essence makes this equivalent to a HALT command in other processors since the execution of commands demands so many clock cycles per instruction; stopping them is the same as a stopping the program. It also means that the final tally for the flags will be N =, 0, Z = 0, V = 0 and C = 0.

Now why did we take this long route to see what was going on? We did it so that a better understanding of what changes the flags and how they behave under certain conditions. It is these conditions we monitor to use the branch commands that are available to us on the 68HC11, and it is this very idea we shall continue with in the next article. However, before we get to that subject, hopefully this little exercise has made some impact. It was meant to show what affects the flags, and should have allowed us a bit of an insight into changing the states when we need to, and what to watch to make sure they are doing just that. But just in case there are questions, you could contact me at brucec@cie-wc.edu for verification of thought or clarification of concept. I thank you for reading, and hope you will join me on our journey to where the branching gets useful. Until then, “Live long and Prosper.”

Leave your comment