16 Bit operations are important for all 8 bit microcontrollers and processors. Here is my take on the basic operations, note that I have not shown how 16 Bit variables are declared and that I am assuming they are declared in Intel Format (low byte/high byte). 
  1. Incrementing and Decrementing 
  2. Addition and Subtraction 
  3. Multiplication 
  4. Division 

1. Incrementing and Decrementing

Incrementing and Decrementing a 16 Bit Number can be described using "C" before actually writing 8051 code. 

Incrementing can be represented as: 

  if ( ++Low == 0       //  Increment the Low eight bits

    High++;             //  If ++Low == 0 then Increment High Byte

             
Which can be coded in 8051 assembler as: 
  inc    Low              ;  Increment the Low eight bits

  mov    A,Low            ;  If ++Low != 0 

  jnz    IncSkip          ;   Then Skip Over

  inc    High               ;  Increment the High Byte

IncSkip:

             
If the 16 bit variable is in a bank register, the "cjne" instruction can be used to simplify the operation: 
 

  cjne Rlow, #0FFh, IncSkip     

  inc    Rhigh            ;  If Low == 0FFh, then Increment High

IncSkip:

  inc    RLow             ;  Increment the Low 8 bits

             
Decrement requires the test for the low byte equal to zero and if it is, then the high byte has to be decremented as well. 
  mov    A,Low            ;  If Low != 0 

  jnz    DecSkip          ;   Then Skip Over

  dec    High               ;  Decrement the High Byte

DecSkip:

  inc    Low              ;  Decrement the Low eight bits

             
These snippets will increment the 16 bit variable without affecting the carry flag. 


2. Addition and Subtraction

Addition and Subtraction are very easy in the 8051 with the "adc" (add with Carry) and "subb" (subtract with Borrow) Instructions. The general form can be expressed as: 
  Result = VarA + VarB

             
With 16 Bit the addition being: 
  mov  A,VarA           ;  Add the Lower 8 Bits First

  add  A,VarB

  mov  Result,A

  mov  A,VarA+1         ;  Add the Upper 8 Bits with Carry

  adc  A,VarB+1

  mov  Result+1,A

             
Subtraction is similar, but the carry flag is reset before the first subtraction (there is no subtract without carry instruction): 
  mov  A,VarA           ;  Do the Lower 8 Bits

  clr  C

  subb A,VarB

  mov  Result,A

  mov  A,VarA+1         ;  Do the Upper 8 Bits

  subb A,VarB+1

  mov  Result+1,A

             

3. Multiplication

The 8 bit multiply instruction allows me to create a 16 Bit multiply based on the polynomial multiplication equation: 
  Product = ( A + B ) * ( C + D )

          = A*C + A*D + B*C + B*D

             
and knowing that a 16 bit number can be represented as the following polynominal: 
  16BitNumber = LowByte + ( HighByte * 0100h )

             
The advantages of using the code below for a 16 bit multiply is the constant number of cycles the operation always takes. 
  mov  Product+3,#0           ;  Clear the Most Significant Byte



  mov  A,VarA                 ;  Do "A*C" Multiply

  mov  B,VarB

  mul ab

  mov  Product,A              ;  Store the 16 bit Product

  mov  Product+1,B



  mov  A,VarA                 ;  Do "A*D" Multiply

  mov  B,VarB+1

  mul  ab

  mov  Product+2,B            ;  Store the High Byte of Product

  add  A,Product+1            ;  Now, Add the Low Byte to previous

  mov  Product+1,A            ;   Product

  jnc  MulInside

  inc  Product+2              ;  If Result is Greater than 0FFh, 

  mov  A,Product+2            ;   Increment Third Byte

  jnz  MulInside

  inc  Product+3              ;  If 3rd Byte == 0 after Increment, 

                              ;   then Increment High Byte



MulInside:                    ;  Do "B*C" Multiply

  mov  A,VarA+1

  mov  B,VarB

  mul  ab

  add  A,Product+1            ;  Add the Lower Byte

  mov  Product+1,A

  mov  A,B                    ;  Add the Upper Byte

  adc  A,Product+2

  mov  Product+2,A

  jnc  MulLast                ;  If No Carry, then Don't Increment 

  inc  Product+3              ;   High Value



MulLast:                      ;  Do "B*D Multiply

  mov  A,VarA+1

  mov  B,VarB+1

  mul  ab

  add  A,Product+2            ;  Add Low 8 Bits of Product to 

  mov  Product+2              ;   the Result

  mov  A,B                    ;  Add the High Byte Finally

  adc  A,Product+3              

  mov  Product+3,A

             
Note that the product is 32 bits. 

4. Division

Rather than carry out division by using repeated subtraction, I shift the divisor until it is greater than the diviend and then shift down and subtract if the Divisend is less than or equal to the divisor: 
  Count = 0;                  //  Keep Track of the Shifting Bits

  Quotient = 0;



  while ( Dividend > Divisor ) {        

    Divisor = Divisor << 1;   //  Find the Shifted Divisor that is 

    Count++;                  //   Greater than the Dividend

  }



  if ( Count != 0 ) {         //  Did we Actually Shift Something?



    Divisor = Divisor >> 1;   //  Move the Divisor Back Down to  

    Count--;                  //   where it can be Used



    while ( Count != 0 ) {    //  Now, Find the Quotient

      if ( Dividend >= Divisor ) {  //  Dividend - Divisor?

        Dividend = Dividend - Divisor;

        Quotient += ( 1 << Count ); //  Update the Quotient with 

      }                       //   the Divisor Shift Value

      Divisor = Divisor >> 1; //  Take Down Divisor by Power of 2

      Count--;

    }

  }

             
Converting this to 8051 assembler, I came up with: 
  mov  Count,#1               ;  Keep Track of the Current Bit in 

  mov  Count+1,#0             ;   "Count" Variable

  mov  Quotient,#0            ;  Clear the Quotient (Nothing Taken 

  mov  Quotient+1,#0          ;   Away yet)



DivLoop1:                     ;  First Loop - See How Far to Shift 

                              ;   Divisor

  mov  A,Dividend             ;  Subtract the Divisor and Check 

  clr  C                      ;   for the Carry Flag Set

  subb A,Divisor                        

  mov  A,Dividend+1           ;  Now, See what the High Value for 

  subb A,Divisor+1            ;   Results in

  jc   DivEnd1                ;  If Carry Set - Stop Shifting 

                              ;   Divisor

  mov  A,Divisor              ;  Shift Divisor Up by 1

  rlc  a

  mov  Divisor,A

  mov  A,Divisor+1

  rlc  a

  mov  Divisor+1,A



  mov  A,Count                ;  "Increment" Count by Shifting 

  clr  C                      ;   Up

  rlc  Count

  mov  Count,A

  mov  A,Count+1

  rlc  Count

  mov  Count+1,A  



  ajmp DivLoop1               ;  Repeat the operation



DivEnd1:                      ;  Have Shifted the Divisor to 

                              ;   a Value Greater than the 

                              ;   Dividend, Take it Down

  mov  A,Count                ;  Did we Actually Shift?

  dec  A

  jz   DivEnd                 ;  If Count is Still at the 

  clr  C                      ;   First Bit, No

  mov  A,Divisor+1

  rrc  a

  mov  Divisor+1,A

  mov  A,Divisor

  rrc  a

  mov  Divisor,A



  mov  A,Count+1              ;  "Decrement" the Count

  clr  C

  rrc  a

  mov  Count+1,A

  mov  A,Count

  rrc  a

  mov  Count,A



DivLoop2:                     ;  Now, Get the Count 



  mov  A,Dividend             ;  Can We Take Away the 

  clr  C                      ;   Divisor?

  sub  A,Divisor

  mov  B,A                    ;  Save Low 8 Bits for Later

  mov  A,Dividend+1           ;  Take Away the High 8 Bits

  subb A,Divisor+1

  jc   DivSkip2               ;  If Carry, Dividend is Less 

                              ;   than Divisor

  mov  Dividend,B             ;  Save the New Divisor

  mov  Dividend+1,A



  mov  A,Quotient             ;  Update the Quotient

  add  A,Count

  mov  Quotient,A

  mov  A,Quotient+1

  add  A,Count+1

  mov  Quotient+1,A



DivSkip2:                     ;  Now, Take Down Divisor 

                              ;   and Count

  mov  A,Divisor+1            ;  Shift Divisor by 1 bit

  clr  C

  rrc  a

  mov  Divisor+1,A

  mov  A,Divisor

  rrc  a

  mov  Divisor,A



  mov  A,Count+1              ;  Take down the Bit Value

  clr  C

  rrc  a

  mov  Count+1,A

  mov  A,Count

  rrc  a

  mov  Count,A



  jnc  DivLoop2               ;  If Carry Not Loaded, 

                              ;   Loop Around Again

             
The remainder is left in the "Dividend" Variable. 

copyright © Myke Predko 1998