Subtraction
Subtraction can be done by taking the 2’s complement of the number to be subtracted, the subtrahend, and adding it to another number, the minuend. The 8051, however, has commands to perform direct subtraction of two signed or unsigned numbers. Register A is the destination address for subtraction. All four addressing modes may be used for source addresses. The commands treat the carry flag as a borrow and always subtract the carry flag as part of the operation.
The following table lists the subtract mnemonics.
Mnemonic |
Operation |
SUBB A,#n |
Subtract immediate number n and the C flag from A; put the result in A |
SUBB A,add |
Subtract the contents of add and the C flag from A; put the result in A |
SUBB A,Rr |
Subtract Rr and the C flag from A; put the result in A |
SUBB A,@Rp |
Subtract the contents of the address in Rp and the C flag from A; put the result in A |
Note that the C flag is set if a borrow is needed into bit 7 and reset otherwise. The AC flag is set if a borrow is needed into bit 3 and reset otherwise. The OV flag is set if there is a borrow into bit 7 and not bit 6 or if there is a borrow into bit 6 and not bit 7. As in the case for addition, the OV Flag is the XOR of the borrows into bit positions 7 and 6.
Unsigned and Signed Subtraction
Again, depending on what is needed, the programmer may choose to use bytes as signed or unsigned numbers. The carry flag is now thought of as a borrow flag to account for situations when a larger number is subtracted from a smaller number. The OV flag indicates results that must be adjusted whenever two numbers of unlike signs are subtracted and the result exceeds the planned signed magnitudes.
Unsigned Subtraction
Because the C flag is always subtracted from A along with the source byte, it must be set to 0 if the programmer does not want the flag included in the subtraction. If a multi-byte subtraction is done, the C flag is cleared for the first byte and then included in subsequent higher byte operations.
The result will be in true form, with no borrow if the source number is smaller than A, or in 2’s complement form, with a borrow if the source is larger than A. These are not signed numbers, as all eight bits are used for the magnitude. The range of numbers is from positive 255d (C = 0, A= FFh) to negative 255d (C = I, A= 0th).
The following example demonstrates subtraction of larger number from a smaller number:
015d = 0000111 lb
SUBB 100d = 01 01100100b
-085d 1 10101011b= 171d
The C flag is set to I, and the OV flag is set to 0. The 2’s complement of the result is 085d.
The reverse of the example yields the following result:
100d = 01100100b
015d = 00001111b
085d 01010101b = 085d
The C flag is set to 0, and the OV flag is set to 0. The magnitude of the result is in true form.
Signed Subtraction
As is the case for addition, two combinations of unsigned numbers are possible when subtracting: subtracting numbers of like and unlike signs. When numbers of like sign are subtracted, it is impossible for the result to exceed the positive or negative magnitude limits of +I 27d or – I 28d, so the magnitude and sign of the result do not need to be adjusted, as shown in the following example:
+ 100d = 01100100b (Carry flag = 0 before SUBB)
SUBB +126d = 0111110b
-026d 11100110b = -026d
There is a borrow into bit positions 7 and 6; the carry flag is set to I, and the OV flag is cleared.
The following example demonstrates using two negative numbers:
-061d = 1100001 lb (Carry flag = 0 before SUBB)
SUBB -116d = 11000011b
+055d 00110111b = +55d
There are no borrows into bit positions 6 or 7, so the OY and carry flags are cleared to zero.
An overflow is possible when subtracting numbers of opposite sign because the situation becomes one of adding numbers of like signs, as can be demonstrated in the following example:
-099d = 10011101b (Carry flag = 0 before SUBB)
SUBB + 100d = 01 01100100b
-199d 00111001b = +057d
Here, there is a borrow into bit position 6 but not into bit position 7; the OV flag is set to I, and the carry flag is cleared to 0. Because the OV flag is set to I, the result must be adjusted. In this case, the magnitude can be interpreted as the 2’s complement of 7 ld, the remainder after a carry out of l 28d from I 99d. The magnitude is correct, and the sign needs to be corrected to a 1 .
The following example shows a positive overflow:
+087d = 0101011 lb (Carry flag = 0 before SUBB)
SUBB -052d = 11001100b
+139d 1000101 lb = -I 17d
There is a borrow from bit position 7, and no borrow from bit position 6; the OV flag and the carry flag are both set to I. Again the answer must be adjusted because the OV flag is set to one. The magnitude can be interpreted as a +01 Id, the remainder from a carry out of I 28d. The sign must be changed to a binary 0 and the OV condition dealt with.
The general rule is that if the OV flag is set to I, then complement the sign bit. The OV flag also signals that the result is greater than – I 28d or +I 27d.
Again, it must be emphasized: When an overflow occurs in a program, an error has been made in the estimation of the largest number needed to successfully operate the program. Theoretically, the program could resize every number used, but this extreme procedure would tend to hinder the performance of the microcontroller.
Note that for all the examples in this section, it is assumed that the carry flag = 0 before the SUBB. The carry flag must be 0 before any SUBB operation that depends upon C = 0 is done.
The following table lists examples of SUBB multiple-byte signed arithmetic operations:
Mnemonic |
Operation |
MOV 0D0h,#00h |
Carry flag= 0 |
MOV A,#3Ah |
A= 3Ah |
MOV 45h,#13h |
Address 45h = 13h |
SUBB A,45h |
A = 27h; C = 0, OV = 0 |
SUBB A,45h |
A = 14h; C = 0, OV = 0 |
SUBB A,#80h |
A = 94h; C = 1 , OV = 1 |
SUBB A,#22h |
A= 71h; C = 0, OV = 0 |
SUBB A,#0FFh |
A = 72h; C = 1 , OV = 0 |
CAUTION
Remember to set the carry flag to zero if it is not to be included as part of the subtraction operation.
Multiplication and Division
The 8051 has the capability to perform 8-bit integer multiplication and division using the A and B registers. Register B is used solely for these operations and has no other use except as a location in the SFR space of RAM that could be used to hold data. The A register holds one byte of data before a multiply or divide operation, and one of the result bytes after a multiply or divide operation.
Multiplication and division treat the numbers in registers A and B as unsigned. The programmer must devise ways to handle signed numbers.
Multiplication
Multiplication operations use registers A and B as both source and destination addresses for the operation. The unsigned number in register A is multiplied by the unsigned number in register B, as indicated in the following table:
Mnemonic |
Operation |
MULAB |
Multiply A by B: put the low-order byte of the product in A, put the high-order byte in B |
The OV flag will be set if Ax B > FFh. Setting the OV flag does not mean that an error has occurred. Rather, it signals that the number is larger than eight bits, and the programmer needs to inspect register B for the high-order byte of the multiplication operation. The carry flag is always cleared to 0.
The largest possible product is FEO I h when both A and B contain FFh. Register A contains 0lh and register B contains FEh after multiplication of FFh by FFh. The OV flag is set to 1 to signal that register B contains the high-order byte of the product; the carry flag is 0.
The following table gives examples of MUL multiple-byte arithmetic operations:
Mnemonic |
Operation |
MOV A,#7Bh |
A= 7Bh |
MOV OFOh.#02h |
B = 02h |
MULAB |
A = 00h and B = F6h: OV Flag = 0 |
MOV A,#0FEh |
A = FEh |
MULAB |
A = 14h and B = F4h: OV Flag =1 |
CAUTION
Note there is no comma between A and 8 in the MUL mnemonic.
Division
Division operations use registers A and B as both source and destination addresses for the operation. The unsigned number in register A is divided by the unsigned number in register B, as indicated in the following table:
Mnemonic |
Operation |
DIV AB |
Divide A by B; put the integer part of quotient in register A and the integer part of the remainder in B |
The OV flag is cleared to 0 unless B holds 00h before the DIV. Then the 0V flag is set to I to show division by 0. The contents of A and B, when division by 0 is attempted, are undefined. The carry flag is always reset.
Division always results in integer quotients and remainders, as shown in the following example:
When done in hex:
The following table lists examples of DIV multiple-byte arithmetic operations:
Mnemonic |
Operation |
MOV A,#0FFh |
A = FFh (255d) |
MOV 0F0h,#2Ch |
B = 2C (44d) |
DIV AB |
A = 05h and B = 23h [255d = (5 x 44) + 35] |
DIV AB |
A = 00h and B = 05h (05d = (0 x 35) + 5] |
DIV AB |
A = 00h and B = 00h [00d = (0 x 5) + 0J |
DIV AB |
A = ?? and B = ??; OV flag is set to one |
CAUTION
The original contents of B (the divisor) are lost.
Note there is no comma between A and B in the DIV mnemonic.
Decimal Arithmetic
Most 8051 applications involve adding intelligence to machines where the hexadecimal numbering system works naturally. There are instances, however, when the application involves interacting with humans, who insist on using the decimal number system. In such cases, it may be more convenient for the programmer to use the decimal number system to represent all numbers in the program.
Four bits are required to represent the decimal numbers from 0 to 9 (0000 to 1001) and the numbers are often called Binary coded decimal (BCD) numbers. Two of these BCD numbers can then be packed into a single byte of data.
The 8051 does all arithmetic operations in pure binary. When BCD numbers are being used the result will often be a non-BCD number, as shown in the following example:
Note that to adjust the answer, an 06d needs to be added to the result.
The opcode that adjusts the result of BCD addition is the decimal adjust A for addition (DA A) command, as shown in the following table:
Mnemonic |
Operation |
DA A |
Adjust the sum of two packed BCD numbers found in A register; leave the adjusted number in A. |
The C flag is set to I if the adjusted number exceeds 99BCD and set to 0 otherwise. The DA A instruction makes use of the AC flag and the binary sums of the individual binary nibbles to adjust the answer to BCD. The AC flag has no other use to the programmer and no instructions-other than a MOV or a direct bit operation to the PSW-affect the AC flag.
It is important to remember that the DA A instruction assumes the added numbers were in BCD before the addition was done. Adding hexadecimal numbers and then using DA A will not convert the sum to BCD.
The DA A opcode only works when used with ADD or ADDC opcodes and does not give correct adjustments for SUBB, MUL or DIV operations. The programmer might best consider the ADD or ADDC and DA A as a single instruction and use the pair automatically when doing BCD addition in the 8051.
The following table gives examples of BCD multiple-byte arithmetic operations:
Mnemonic |
Operation |
MOV A,#42h |
A= 42BCD |
ADD A,#13h |
A= 55h; C = 0 |
DA A |
A= 55h; C = 0 |
ADD A,#17h |
A= 6Ch; C = 0 |
DA A |
A = 72BCD; C = 0 |
ADDC A,#34h |
A= A6h; C = 0 |
DA A |
A = 06BCD; C =1 |
ADDC A,#1 lh |
A = l SBCD; C = 0 |
DA A |
A = !&BCD; C = 0 |
CAUTION
All numbers used must be in BCD form before addition. Only ADD and ADDC are adjusted to BCD by DA A.
Example Programs
The challenge of the programs presented in this section is writing them using only opcodes that have been covered to this point in the book. Experienced programmers may long for some of the opcodes to be covered in Chapter 6, but as we shall see, programs can be written without them.
EXAMPLE PROBLEM 5.1
Add the unsigned numbers found in internal RAM locations 25h, 26h, and 27h together and put the result in RAM locations 30h (MSB) and 3lh (LSB).
• Thoughts on the Problem The largest number possible is FFh + FFh = 01 FEh + FFh = 02FDh, so that two bytes will hold the largest possible number. The MSB will be set to 0 and any carry bit added to it for each byte addition.
To solve this problem, use an ADD instruction for each addition and an ADDC to the MSB for each carry which might be generated. The first ADD will adjust any carry flag which exists before the program starts.
The complete program is shown in the following table:
Mnemonic |
Operation |
MOV 3Jh,#00h |
Clear the MSB of the result to 0 |
MOV A,25h |
Get the first byte to be added from location 25h |
ADD A,26h |
Add the second byte found in RAM location 26h |
MOV R0,A |
Save the sum of the first two bytes in RO |
MOV A,#00h |
Clear A to 00 |
ADDC A,3lh |
Add the carry to the MSB; carry = 0 after this operation |
MOV 3lh,A |
Store MSB |
MOV A,R0 |
Get partial sum back |
ADD A,27h |
Form final LSB sum |
MOV 39h,A |
Store LSB |
MOV A,#00h |
Clear A for MSB addition |
ADDC A,31h |
Form final MSB |
MOV 3lh,A |
Store final MSB |
COMMENT
Notice how awkward it becomes to have to use the A register for all operations. Jump instructions, which will be covered in Chapter 6, require less use of A.
EXAMPLE PROBLEM 5.2
Repeat problem 5.1 using BCD numbers.
• Thoughts on the Problem The numbers in the RAM locations must be in BCD before the problem begins. The largest number possible is 99d + 99d = I 98d + 99d = 297d, so that up to two carries can be added to the MSB.
The solution to this problem is identical to that for unsigned numbers, except a DA A must be added after each ADD instruction. If more bytes were added so that the MSB could exceed 09d, then a DA A would also be necessary after the ADDC opcodes.
The complete program is shown in the following table:
Mnemonic |
Operation |
MOV 3lh,#00h |
Clear the MSB of the result to 0 |
MOV A,25h |
Get the first byte to be added from location 25h |
ADD A,26h |
Add the second byte found in RAM location 26h |
DAA |
Adjust the answer to BCD form |
MOV RO,A |
Save the sum of the first two bytes in RO |
MOV A,#OOh |
Clear A to 00 |
ADDC A,3lh |
Add the carry to the MSB; carry = 0 after this operation |
MOV 31h,A |
Store MSB |
MOV A,RO |
Get partial sum back |
ADD A,27h |
Form final LSB sum |
DAA |
Adjust the final sum to BCD |
MOV 30h,A |
Store LSB |
MOV A,#Ooh |
Clear A for MSB addition |
ADDC A,3lh |
Form final MSB |
MOV 31h,A |
Store final MSB |
COMMENT
When using BCD numbers, DA A can best be thought of as an integral part of the ADD instructions.
EXAMPLE PROBLEM 5.3
Multiply the unsigned number in register R3 by the unsigned number on port 2 and put the result in external RAM locations 10h (MSB) and 11 h (LSB).
• Thoughts on the Problem The MUL instruction uses the A and B registers; the problem consists of MOV es to A and B followed by MOVes to the external RAM. The complete program is shown in the following table:
Mnemonic |
Operation |
MOV A,0A0h |
Move the port 2 pin data to A |
MOV 0F0h,R3 |
Move the data in R3 to the B register |
MULAB |
Multiply the data; A has the low order result byte |
MOV R0.#1 lh |
Set RO to point to external RAM location l lh |
MOV@R0,A |
Store the LSB in external RAM |
DEC R0 |
Decrement R0 to point to !0h |
MOV A,0F0h |
Move B to A |
MOV@R0,A |
Store the MSB in external RAM |
coMMENT
Again we see the bottleneck created by having to use the A register for all external data transfers.
More advanced programs which do signed math operations and multi-byte multiplication and division will have to wait for the development of Jump instructions in Chapter 6.
Summary OF 8051 Arithmetic Operations
The 8051 can perform all four arithmetic operations: addition, subtraction. multiplication, and division. Signed and unsigned numbers may be used in addition and subtraction; an 0Y flag is provided to signal programmer errors in estimating signed number magnitudes needed and to adjust signed number results. Multiplication and division use unsigned numbers. BCD arithmetic may be done using the DA A and ADD or ADDC instructions.
The following table lists the arithmetic mnemonics:
Mnemonic |
Operation |
ADD A. source |
Add the source byte to A; put the result in A and adjust the C and OY flags |
ADDC A, source |
Add the source byte and the carry to A; put the result in A and adjust the C and OV flags |
DA A |
Adjust the binary result of adding two BCD numbers in the A register to BCD and adjust the carry flag |
DEC source |
Subtract a I from the source; roll from 00h to FFh |
DIV AB |
Divide the byte in A by the byte in B; put the quotient in A and the remainder in B; set the OV flag to I if B = 00h before the division |
INC source |
Add a 1 to the source; roll from FFh or FFFFh to 00h or 0000h |
MULAB |
Multiply the bytes in A and B; put the high-order byte of the result in B, the low-order byte in A; set the OV flag to 1 if the result is > FFh |
SUBB A. source |
Subtract the source byte and the carry from A; put the result in A and adjust the C and OV flags |
Problems
Write programs that perform the tasks listed using only opcodes that have been discussed in this and previous chapters. Use comments on each line of code and try to use as few lines as possible. All numbers may be considered to be unsigned numbers.
l. Add the bytes in RAM locations 34h and 35h: put the result in register R5 (LSB) and R6 (MSB).
2- Add the bytes in registers R3 and R4; put the result in RAM location 4Ah (LSB) and 4Bh (MSB).
3. Add the number 84h to RAM locations l 7h and 18h.
4. Add the byte in external RAM location 02CDh to internal RAM location l9h; put the result into external RAM location 0000h (LSB) and 00C1h (MSB).
5-8. Repeat Problems 1-4, assuming the numbers are in BCD format.
9. Subtract the contents of R2 from the number F3h; put the result in external RAM location 028Bh.
10. Subtract the contents of R l from R0; put the result in R7.
11. Subtract the contents of RAM location 13h from RAM location 2Bh: put the result in RAM location 3Ch.
12. Subtract the contents of TH0 from TH I: put the result in TL0.
13. Increment the contents of RAM location 13h. 14h. and 15h using indirect addressing only.
14. Increment TLI by I Oh.
15. Increment external RAM locations 0100h and 0200h.
16. Add a l to every external RAM address from 00h to 06h.
17. Add a l to every external RAM address from 0100h to 0106h.
18. Decrement TL0. THO, TL1, and TH l.
19. Decrement external RAM locations 0l23h and 01 BDh.
20. Decrement external RAM locations 45h and 46h.
21. Multiply the data in RAM location 22h by the data in RAM location l Sh; put the result in RAM locations 19h (low byte). and lAh (high byte).
22. Square the contents of R5; put the result in R0 (high byte), and RI (low byte).
23. Divide the data in RAM location 3Eh by the number l 2h; put the quotient in R4 and the remainder in R5.
24. Divide the number in RAM location 1 Sh by the data in RAM location 16h; put the result in external RAM location 7Ch.
25. Divide the data in RAM location 13h by the data in RAM location 14h, then restore the original data in l3h by multiplying the answer by the data in 14h.