Experiment 2 - Arithmetic Operations

In Experiment 1, we saw how to move data into, out of, and around the Intel 8080's working registers.  In Experiment 2, we explore the Intel 8080's arithmetic instructions.  The Intel 8080 has instructions for two basic arithmetic operations, 8/16-bit addition and 8-bit subtraction.  This may seem rather limiting, but, using only these arithmetic instructions, programs can be written to handle large (even decimal) numbers and perform operations like multiplication and division.

As we explore the Intel 8080 ADD/SUB group of instructions, here are some things to keep in mind.

 Addition with the Intel 8080.

Intel 8080 addition instructions add two values, one in register A and the other either (1) in a working register using ADD and ADC instructions or (2) in the immediate (following) data byte using ADI and ACI instructions.  ADD and ADI ignore the Carry Bit; ADC and ACI add the Carry Bit into the sum.  Let's look first at the immediate addition instructions ADI and ACI. 

Note: In the descriptions below, r is the Register Reference Number and v is an 8-bit data value.

ADI v    Add Immediate to A    v is added to the value in A with result in A.  The code byte for ADI is 306Q and is followed by a data byte of value v. 
Status bits affected - Carry, Sign, Zero, Parity, Auxiliary Carry

ACI v    Add Immediate to A with Carry    v and the carry bit are added to the value in A with result in A.  The code byte for ACI is 316Q and is followed by a data byte of value v. 
Status bits affected - Carry, Sign, Zero, Parity, Auxiliary Carry

"Status bits affected" means that, after the instruction is executed, the listed status bits in the Processor Status Word (PSW) will change to reflect the result of the operation.  Status bits not listed will be unaffected.  For instance, if the sum exceeds 255, the 8-bit maximum value, the Carry Bit is set.  For a thorough explanation of the status bits and the PSW refer to page 5 of the Intel 8080 Assembly Language Programming Manual.

Try it!

Before trying the first example, let's take a moment to describe how machine code will be presented.  Generally, each line will consist of four columns as shown below.

  1. Split octal address
  2. Code byte or data byte in octal
  3. Intel 8080 assembly assembly mnemonic for the instruction
  4. Comment

The "Comment" column contains information about the line, often what the code byte does operationally.  We will use a pseudo-code that I developed early when working with the Intel 8080.  It's self-explanatory for the most part. For instance, an arrow (g) is used to indicate that a value or the result of an operation is stored in a register.  An equal sign "=" means exactly that; one value equals another such as 25=031Q.

Example 1 - Create and test code that adds an immediate data value using ADI v.

000 000    076    MVI A, 25    25gA
000 001    031                         25= 031Q
000 002    306    ADI 12          A+12gA
000 003    014                         12= 014Q
000 004    166    HLT              Halt execution

a) Enter the code above to add 25 and 12. Click Reset to get back to address 000 000Q then Single Step through the program.  Click Display A to check the value in the accumulator.  It should be 25 + 12 = 37 = 045Q.  Now click Display Status.  D0 (Bit 0 of the status bits) is the Carry Bit.  It is cleared (zero) indicating the sum was within the range of the 8-bit accumulator (register A).

b) Change the immediate value in address 000 003Q from 12 (014Q) to 245 (365Q).  Reset and this time, click Run.  The code will execute and halt at address 000 004Q.  Click Display A.  It is 016Q or 14, not the expected result.  The correct value is 25+245=270.  The problem is that the largest value A register can hold is 377Q (all bits 1) or 255.  Click Display Status and note that the Carry Bit (D0) is now set (one).  The Carry Bit set indicates arithmetic overflow into the next larger bit position, 28 = 256 in this case. If we add 256 and 14, we get 270, the expected result.  We can use the Carry Bit to do multi-byte addition.

Example 2 - Add 25 and 12 with ACI so that the Carry Bit will be added into the sum.

000 000    067    STC               Set Carry Bit (Carry Bit = 1)
000 001    077    CMC             Complement Carry Bit to clear it (Carry Bit = 0)
000 002    076    MVI A, 25     25gA
000 003    031                          25=031Q
000 004    316    ACI 12          A+12+CgA
000 005    014                          12=014Q
000 006    166    HLT               Halt execution

In this example we use ACI, a variant of the ADI instruction, that adds in the Carry Bit to bit 0 of the sum.  We use the STC (Set Carry) instruction to set the Carry Bit making it one and follow with the CMC (Complement Carry) instruction to "flip" the Carry Bit making it zero. 

Load and run the code then click Display A.  The Data LED's should display 045 octal, which is the expected result 37.  Note that the Carry Bit is cleared and did not affect the sum.  Now replace the CMC instruction at location 000 001Q with 000Q, a NOP (No Operation) instruction that does nothing except waste a little time.  Run the program again and display the result.  It should be 046Q or 38 because the Carry Bit, now set, has been added to the sum.

The ADD and ADC instructions work similarly except the value to be added to A is in one of the working registers.

ADD r    Add contents of register r to A    The contents of register r is added to the value in A with result in A.  The octal code byte for ADD is 20r.  For example, ADD H is 204Q,  "4" being the Register Reference Number for register H.
Status bits affected - Carry, Sign, Zero, Parity, Auxiliary Carry

ADC r    Add contents of register r to A with Carry    The contents of register r and the carry bit are added to the value in A with result in A.  The octal format of ADD is 21r.  For example, ADC H is 214Q. 
Status bits affected - Carry, Sign, Zero, Parity, Auxiliary Carry

Try it yourself!

Rewrite and test Examples 1 and 2 using ADD and ADC instructions respectively.  Instead of immediate values, use MVI E,v to load the data 12 into the E register then use ADD E or ADC E to complete the addition.

Something to think about!

ADD A is a valid instruction.  It is a simple way to multiply A by 2; that is, A + A = 2 * A.  Also, multiplying by 2 or doubling shifts all the bits in A one place to the left, which is another useful way of thinking of doubling.

 Subtraction with the Intel 8080.

SUB, SBB, SUI, and SBI are the subtraction counterparts to ADD, ADC, ADI, and ACI.   The minuend is in the A register and the subtrahend (value to be subtracted from the minuend) is either (1) in a working register for SUB and SBB instructions or (2) n an immediate (following) data byte for SUI and SBI instructions.  The Carry Bit in this case acts as a Borrow Bit that is set to one if the subtrahend is larger than the minuend.  SUB and SUI ignore the Borrow Bit; SBB and SBI subtract the Borrow Bit along with the subtrahend.  Let's look first at the immediate subtraction instructions SUI and SBI.

SUI v    SUI Immediate v from A    v is subtracted from the value in A with result in A.  The code for SBI is 326Q. 
Status bits affected - Carry, Sign, Zero, Parity, Auxiliary Carry

SBI v    Subtract Immediate v  from A with Carry    v and the carry bit are subtracted the value in A with result in A.  The code for SBI is 336Q. 
Status bits affected - Carry, Sign, Zero, Parity, Auxiliary Carry

The Intel 8080 uses two's complement arithmetic (TCA) for signed numbers.  This permits us to easily handle the addition and subtraction of negative numbers.  With TCA in an 8-bit byte, bit 7 becomes a sign bit and is lost for numeric value.  What was a 0 to 255 unsigned number range becomes a signed number range -128 to +127.  By using multi-byte TCA representation, however, the range of signed numbers can be increased to whatever is desired.  The best part of using TCA is that all variations of adding and subtracting both positive and negative numbers can be accomplished directly with the ADD/SUB group of Intel 8080 instructions.  For instance, 25 + (-18) using the ADD instruction produces the same result as 25 - (+18) using the SUB instruction.  The table below illustrates how twos complement numbers are assigned. It's not what we might think: bit 7 the sign and bits 0-6 representing 128 to give +/- 128!  Instead, it's more like a car odometer rolling forward and backward around zero.

Decimal Number TCA Binary Number
127 01 111 111
126 01 111 110
125 01 111 101
... ...
3 00 000 011
2 00 000 010
1 00 000 001
0 00 000 000
-1 11 111 111
-2 11 111 110
-3 11 111 101
... ...
-126 10 000 010
-127 10 000 001
-128 10 000 000

One nifty thing about TCA is that a number can be negated by simple complementing it and adding 1.  Take -126 = 10 000 010.  For -(-126), complement each bit giving 01 111 101 then add 1.  We get 01 111 110, the expected result 126 from the above table.

Try it!

Example 3 - Create and test code that subtracts a value using SUI v.

000 000    076    MVI A, 25    25gA
000 001    031                          25=031Q
000 002    326    SUI 18          A-12gA
000 003    014                         12=014Q
000 004    166    HLT              Halt execution

a) Enter the code to subtract 12 from 25. Click Reset and Single Step through the program.  Use the Display A button to check the value in the accumulator.  It should be 015Q or 13.  Now click Display Status.  The Borrow (Carry) Bit should be zero since no borrow was needed.

 b) Subtract 25 from 12 by reversing the immediate values in addresses 000 001Q and 000 003Q above.  Reset and this time, click Run.  The code will execute and halt at address 000 004Q.  The correct decimal value  is -13.  Display A should show 363Q or -13, the two's complement of 13.  Click Display Status and see that the Borrow (Carry) Bit is one indicating a borrow was needed.  The Borrow Bit can be used to do multi-byte subtraction.

 Example 4 - Subtract 12 from 25 with SBI so that the Borrow (Carry) Bit will be subtracted along with the immediate value.

000 000    067    STC               Set Carry Bit (C=1)
000 001    077    CMC             Complement Carry Bit (C=0)
000 002    076    MVI A, 25     25gA
000 003    031                          25=031Q
000 004    336    SBI 12           A-12-CgA
000 005    014                          12=014Q
000 006    166    HLT               Halt execution

Load and run the code then click Display A.  It should be 015 or 25-12-0=13.  The zero Borrow (Carry) Bit made no difference.  Replace the CMC as before with a NOP  instruction.  Run the code again and display the result.  It should be 014Q or 25-12-1=12 because the Borrow Bit, now one, has been subtracted along with 12.

The SUB and SBB instructions work similarly except the value to be subtracted from A is in one of the registers.  Note: In the definitions below, r is a Register Reference Number.

SUB r    Subtract contents of register r to A    The contents of register r is subtracted from the value in A with result in A.  The octal code byte for SUB is 22r.  For example, SUB H is 224Q. 
Status bits affected - Carry, Sign, Zero, Parity, Auxiliary Carry

SBB r    Subtract contents of register r to A with Borrow (Carry)    The contents of register r and the carry bit are subtracted from the value in A with result in A.  The octal code byte for SBB is 23r.  Example: SBB H is 234Q. 
Status bits affected - Carry, Sign, Zero, Parity, Auxiliary Carry

Try it yourself!

Rewrite and test Examples 3 and 4 using SUB and SBB instructions respectively.  Instead of immediate values, use MVI L,v to load the data (12) into the L register then use SUB or SBB to complete the subtraction.

Something to think about!

SUB A is a valid instruction.  What is the result in A?  What is the Carry Bit?  Could SUB A be used instead of STC followed by CMC to clear the carry, thus saving a byte?

How to do a two's complement conversion.

Let's look at how Intel 8080 code can do a two's complement conversion.  Assume that the value to be two's complemented is in the A register.  The instruction CMA instruction 057Q complements the A register; that is, 0's become 1's and 1's become 0.  To complete twos complement conversion, simply follow with the ADI 1 instruction. 

Try it !

Example 5 - Create and test code that verifies that the negative of a negative is a positive. 

000 000    076     MVI A, 25      25gA
000 001    031                            25=031Q
000 002    057    CMA                A'gA (The apostrophe indicates the bit-wise complement of A.)
000 003    306    ADI 1               A+1gA
000 004    001                             1=001Q
000 005    166    HLT                  Halt execution

Use the example code above and verify the two's complement value for 25 as shown in the table below.  Then rerun the code starting with -25 to see if -(-25) = 25. 

N
Decimal
N
Octal
N
Binary
-N
Binary
-N
Octal
-N
Decimal
25 031 00 011 001 11 100 110 +1 =
11 100 111
347 -25
-25 347 11 101 110 00 010 001 + 1 = 00 010 010 031 25
        

Example 6 - Create and test code that verifies the variety of addition and subtraction examples shown in the table below.

 Use the code of Example 1 substituting the appropriate instruction (ADI v or SUI v) and the table values to verify the results indicated.

A Operation B = = (in Binary) = (in Octal)
25 + 18 43 00 101 011 053
25 + (-18) 7 00 000 111 007
-25 + 18 -7 11 111 001 371
-25 + -18 -43 11 010 101 325
25 - 18 7 00 000 111 007
-25 - 18 -43 11 010 101 325
-25 - -18 -7 11 111 001 371

Something to think about!

What would happen if we added 125 and 85 using signed numbers?  The result is greater than the two's complement maximum of +127 and would appear to be an erroneous negative number.  This is signed arithmetic overflow.  A similar situation occurs when subtraction yields a value less than -128.  In a later experiment, we will see how to detect this overflow and report it as an error.  Of course, if unsigned numbers arithmetic is assumed, the sum would be 210 and perfectly acceptable.  Overflow in this case could be monitored by the Carry bit.

Continue to next Experiment - Click Here