Introduction
Applications of microcontrollers often involve performing mathematical calculations on data in order to alter program flow and modify program actions. A microcontroller is not designed to be a "number cruncher," as is a general-purpose computer. The domain of the microcontroller is that of controlling events as they change (real-time control). A sufficient number of mathematical opcodes must be provided, however, so that calculations associated with the control of simple processes can be done, in real time, as the controlled system operates. When faced with a control problem, the programmer must know whether the 8051 has sufficient capability to expeditiously handle the required data manipulation. If it does not, a higher performance model must be chosen.
The 24 arithmetic opcodes are grouped into the following types:
Mnemonic |
Operation |
INC destination |
Increment destination by 1 |
DEC destination |
Decrement destination by 1 |
ADDI ADDC destination ,source |
Add source to destination without/with carry (C) flag |
SUBB destination.source |
Subtract, with carry, source from destination |
MUL AB |
Multiply the contents of registers A and B |
DIV AB |
Divide the contents of register A by the contents of register B |
DA A |
Decimal Adjust the A register |
The addressing modes for the destination and source are the same as those discussed in Chapter 3: immediate, register, direct, and indirect.
Flags
A key part of performing arithmetic operations is the ability to store certain results of those operations that affect the way in which the program operates. For example, adding together two one-byte numbers results in a one-byte partial sum, because the 8051 is and eight-bit machine. But it is possible to get a 9-bit result when adding two 8-bit numbers. The ninth bit must be stored also, so the need for a one-bit register, or carry Hag in this case, is identified. The program will then have to deal with the ninth bit, perhaps by adding it to a higher order byte in a multiple-byte addition scheme. Similar actions may have to be taken when a larger byte is subtracted from a smaller one. In this case, a borrow is necessary and must be dealt with by the program.
The 8051 has several dedicated latches, or Hags, that store results of arithmetic operations. Opcodes covered in Chapter 6 are available to alter program flow based upon the state of the Hags. Not all instructions change the Hags. but many a programming error has been made by a forgetful programmer who overlooked an instruction that does change a Hag.
The 8051 has four arithmetic Hags: the carry (C), auxiliary carry (AC), overflow (OV), and parity (P).
Instructions Affecting Flags
The C. AC, and OV Hags are arithmetic Hags. They are set to I or cleared to 0 automatically, depending upon the outcomes of the following instructions. The following instruction set includes all instructions that modify the flags and is not confined to arithmetic instructions:
INSTRUCTION MNEMONIC |
FLAGS AFFECTED |
ADD |
C AC ov |
ADDC |
C AC ov |
ANL C,direct |
C |
CJNE |
C |
CLR C |
C=0 |
CPL C |
C=C |
DA A |
C |
DIV |
C=O OV |
MOV C,direct |
C |
MUL |
C=O OV |
ORL C,direct |
C |
RLC |
C |
RRC |
C |
SETB C |
C = 1 |
SUBB |
C AC OV |
One should remember, however, that the flags are all stored in the PSW. Any instruction that can modify a bit or a byte in that register (MOV. SETB, XCH, etc.) changes the Hags. This type of change takes conscious effort on the part of the programmer.
A Hag may be used for more than one type of result. For example, the C flag indicates a carry out of the lower byte position during addition and indicates a borrow during subtraction. The instruction that last affects a flag determines the use of that flag.
The parity flag is affected by every instruction executed. The P flag will be set to a I if the number of I’s in the A register is odd and will be set to 0 if the number of I’s is even. All O’s in A yield a I’s count of 0, which is considered to be even. Parity check is an elementary error-checking method and is particularly valuable when checking data received via the serial port.
Incrementing and Decrementing
The simplest arithmetic operations involve adding or subtracting a binary I and a number. These simple operations become very powerful when coupled with the ability to repeat the operation-that is, to "INCrement" or "OECrement" -until a desired result is reached.’ Register, Direct, and Indirect addresses may be INCremented or DECremented. No math flags (C, AC, OV) are affected.
The following table lists the increment and decrement mnemonics.
Mnemonic |
Operation |
INC A |
Add a one to the A register |
INC Rr |
Add a one to register Rr |
INC add |
Add a one to the direct address |
INC@Rp |
Add a one to the contents of the address in Rp |
INC DPTR |
Add a one to the lti-bit DPTR |
DEC A |
Subtract a one from register A |
DECRr |
Subtract a one from register Rr |
DEC add |
Subtract a one from the contents of the direct address |
DEC@Rp |
Subtract a one from the contents of the address in register Rp |
Note that increment and decrement instructions that operate on a port direct address alter the latch for that port.
The following table shows examples of increment and decrement arithmetic operations:
Mnemonic |
Operation |
MOV A,#3Ah |
A= 3Ah |
DEC A |
A= 39h |
MOV R0,#15h |
R0= 15h |
MOV 15h,#12h |
Internal RAM address 15h = 12h |
INC@RO |
Internal RAM address 15h = 13h |
DEC I5h |
Internal RAM address 15h = 12h |
INC RO |
R0= 16h |
MOV 16h,A |
Internal RAM address 16h = 39h |
INC@RO |
Internal RAM address 16h = 3Ah |
MOV DPTR,#12 |
DPTR = 12FFh |
FFh INC DPTR |
DPTR = 1300h |
DEC 83h |
DPTR = 1200h (SFR 83h is the DPH byte) |
CAUTION
Remember: No math flags are affected.
All 8-bit address contents overflow from Ffh to 00h.
DPTR is 16 bits; DPTR overflows from FFFFh to 0000h.
The 8-bit address contents underflow from 00h to FFh.
There is no DEC DPTR to match the INC DPTR.
Addition
All addition is done with the A register as the destination of the result. All addressing modes may be used for the source: an immediate number, a register, a direct address, and an indirect address. Some instructions include the carry flag as an additional source of a single bit that is included in the operation at the least significant bit position.
The following table lists the addition mnemonics.
Mnemonic |
Operation |
ADD A.#n |
Add A and the immediate number n; put the sum in A |
ADD A,Rr |
Add A and register Rr; put the sum in A |
ADD A,add |
Add A and the address contents; put the sum in A |
ADD A,@Rp |
Add A and the contents of the address in Rp; put the sum in A |
Note that the C flag is set to I if there is a carry out of bit position 7; it is cleared to 0 otherwise. The AC flag is set to 1 if there is a carry out of bit position 3; it is cleared otherwise. The OV flag is set to I if there is a carry out of bit position 7, but not bit position 6 or if there is a carry out of bit position 6 but not bit position 7, which may be expressed as the logical operation
OV = C7 XOR C6
Unsigned and Signed Addition
The programmer may decide that the numbers used in the program are to be unsigned numbers-that is, numbers that are 8-bit positive binary numbers ranging from 00h to FFh. Alternatively, the programmer may need to use both positive and negative signed numbers.
Signed numbers use bit 7 as a sign bit in the most significant byte (MSB) of the group of bytes chosen by the programmer to. represent the largest number to be needed by the program. Bits 0 to 6 of the MSB, and any other bytes, express the magnitude of the number. Signed numbers use a I in bit position 7 of the MSB as a negative sign and a 0 as a positive sign. Further, all negative numbers are not in true form, but are in 2’s complement form. When doing signed arithmetic. the programmer must know how large the largest number is to be-that is, how many bytes are needed for each number.
In signed form, a single byte number may range in size from 10000000b, which is – I 28d to 01111111 b, which is +I 27d. The number 00000000b is 000d and has a positive sign, so there are 128d negative numbers and 128d positive numbers. The C and OV flags have been included in the 8051 to enable the programmer to use either numbering scheme.
Adding or subtracting unsigned numbers may generate a carry flag when the sum exceeds FFh or a borrow flag when the minuend is less than the subtrahend. The OV flag is not used for unsigned addition and subtraction. Adding or subtracting signed numbers can
lead to carries and borrows in a similar manner, and to overflow conditions due to the actions of the sign bits.
Unsigned Addition
Unsigned numbers make use of the carry flag to detect when the result of an ADD operation is a number larger than Ffb. If the carry is set to one after an ADD, then the carry can be added to a higher order byte so that the sum is not lost. For instance,
95d = 0101111 lb
189d = 10111101b
284d 1 000111 00b = 284d
The C flag is set to I to account for the carry out from the sum. The program could add the carry flag to another byte that forms the second byte of a larger number.
Signed Addition
Signed numbers may be added two ways: addition of like signed numbers and addition of unlike signed numbers. If unlike signed numbers are added, then it is not possible for the result to be larger than -128d or + 127d, and the sign of the result will always be correct. For example,
-001d 11111111b
+027d = 0001 1011b
+026d 0001 1010b = +026d
Here, there is a carry from bit 7 so the carry flag is I. There is also a carry from bit 6, and the OV flag is 0. For this condition, no action need be taken by the program to correct the sum.
If positive numbers are added, there is the possibility that the sum will exceed +I 27d, as demonstrated in the following example:
+ 100d = 01100100b
+050d = 001 10010b
+ 150d 10010110b = -106d
Ignoring the sign of the result, the magnitude is seen to be +22d which would be correct if we had some way of accounting for the + 128d, which, unfortunately, is larger than a single byte can hold. There is no carry from bit 7 and the carry flag is O; there is a carry from bit 6 so the OV flag is 1.
An example of adding two positive numbers that do not exceed the positive limit is:
+045d 00101101b
+075d = 01001011lb
+120d 01111000b = 120d
Note that there are no carries from bits 6 or 7 of the sum; the carry and OV flags are both 0.
The result of adding two negative numbers together for a sum that does not exceed the negative limit is shown in this example:
-030d = 11100010b
-050d = 11001110b
-080d 10110000b = -080d
Here, there is a carry from bit 7 and the carry flag is I; there is a carry from bit 6 and the OV flag is 0. These are the same flags as the case for adding unlike numbers; no corrections are needed for the sum.
When adding two negative numbers whose sum does exceed – l 28d, we have
-070d = 10111010b
-070d = 10111010b
-140d 01110100b = +116d
Or, the magnitude can be interpreted as – l 2d, which is the remainder after a carry out of – l 28d. In this example, there is a carry from bit position 7, and no carry from bit position 6, so the carry and the OV flags are set to I. The magnitude of the sum is correct; the sign bit must be changed to a I.
From these examples the programming actions needed for the C and OV flags are as follows:
FLAGS |
ACTION |
|
C |
OV |
|
0 |
0 |
None |
0 |
1 |
Complement the sign |
1 |
0 |
None |
1 |
1 |
Complement the sign |
A general rule is that if the OV flag is set, then complement the sign. The OV flag also signals that the sum exceeds the largest positive or negative numbers thought to be needed in the program.
Multiple-Byte Signed Arithmetic
The nature of multiple-byte arithmetic for signed and unsigned numbers is distinctly different from single byte arithmetic. Using more than one byte in unsigned arithmetic means that carries or borrows are propagated from low-order to high-order bytes by the simple technique of adding the carry to the next highest byte for addition and subtracting the borrow from the next highest byte for subtraction.
Signed numbers appear to behave like unsigned numbers until the last byte is reached.
Foi a signed number, the seventh bit of the highest byte is the sign; if the sign is negative, then the entire number is in 2’s complement form.
For example, using a two-byte signed number, we have the following examples:
Note that the lowest byte of the numbers 00000 and – 32768d are exactly alike, as are the lowest bytes for+ 32767d and -00001d.
For multi-byte signed number arithmetic, then, the lower bytes are treated as unsigned numbers. All checks for overflow are done only for the highest order byte that contains the sign. An overflow at the highest order byte is not usually recoverable. The programmer has made a mistake and probably has made no provisions for a number larger than planned. Some error acknowledgment procedure, or user notification, should be included in the program if this type of mistake is a possibility.
The preceding examples show the need to add the carry Hag to higher order bytes in signed and unsigned addition operations. Opcodes that accomplish this task are similar to the ADD mnemonics: A C is appended to show that the carry bit is added to the sum in bit position 0.
The following table lists the add with carry mnemonics:
Mnemonic |
Operation |
ADDC A,#n |
Add the contents of A, the immediate number n, and the C Hag; put the sum in A |
ADDC A,add |
Add the contents of A, the direct address contents, and the C Hag; put the sum in A |
ADDC A,Rr A |
Add the contents of A, register Rr, and the C Hag; put the sum in A |
DDC A,@Rp |
Add the contents of A, the contents of the indirect address in Rp, and the C flag; put the sum in A |
Note that the C, AC, and OY flags behave exactly as they do for the ADD commands.
The following table shows examples of ADD and ADDC multiple-byte signed arithmetic operations:
Mnemonic |
Operation |
MOV A,#1Ch |
A= 1Ch |
MOV R5,#0Alh |
R5 = A1h |
ADD A,R5 |
A = BDh; C = 0, OV = 0 |
ADD A,R5 |
A = 5Eh; C = 1, OV = 1 |
ADDC A,#10h |
A = 6Fh; C = 0, OV = 0 |
ADDC A,#IOh |
A = 7Fh; C = 0, OV = 0 |
CAUTION
ADDC is normally used to add a carry after the LSB addition in a multi-byte process. ADD is normally used for the LSB addition.