UMBC CMSC 391 -- Programming Microcontrollers  


16-bit Operations

Actually, 16-bit operations and 32-bit operations are not different, they are simply multi-byte operations. Simply put, we must do everything a sets of 8-bit operations that will combine to produce the correct result. Addition and subtraction are easy. Division gets complex.

Addition

We start by adding the least significant bytes together in a normal addition (ADD). We will have the results and keep the carry. We get the next two bytes and add with the carry (ADDC) and again save the result. We can do the second step as many times as we want to, so by repeating the second step a total of of three times, we do a 32-bit add.

Example

This example ASSUMES that there is no chance of an overflow and that the values are unsigned!
2000:                   .org    0x2000
                   
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;; Equates                                  ;;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   
                   
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;; Code                                      ;;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   
                   begin:
                   
                   ;; 
                   ;; Add 2381h to 1083h and store the results in
                   ;;   Registers R7/R6  
                   ;;
                   ;; In this example, the even numbered register will hold the
                   ;;   least significant byte and the odd numbered register
                   ;;   holds the most significant byte.
                   ;;
                   
2000: 78 81                 mov     R0, #81h        ; value 1
2002: 79 23                 mov     R1, #23h
                            
2004: 7C 83                 mov     R4, #83h        ; value 2
2006: 7D 10                 mov     R5, #10h
                            
2008: E8                    mov     A, R0
2009: 29                    add     A, R1           ; A now holds 04h and the C
                            
200A: FE                    mov     R6, A           ; MOV instructions do not c
                            
200B: EC                    mov     A, R4
200C: 3D                    ADDC    A, R5           ; This adds 23h, 10h and th
                                                    ;   A now contains 34h
200D: FF                    mov     R7, A
                            
                   ;;
                   ;; R7/R6 now holds 3404h
                   ;;
                            
200E: 22                   ret
                   
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;; Data                                      ;;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

subtraction

Really, subtraction is a simple variant on addition. In the old days, you simply complemented the number and added the two numbers together. Now, instead of the add, you subtract and instead of a carry there is a borrow.

Example

2000:                   .org    0x2000
                   
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;; Equates                                  ;;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   
                   
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;; Code                                      ;;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   
                   begin:
                   
                   ;; 
                   ;; Subtract 1083h from  2381h and store the results in
                   ;;   Registers R7/R6  
                   ;;
                   ;; In this example, the even numbered register will hold the
                   ;;   least significant byte and the odd numbered register
                   ;;   holds the most significant byte.  This example assumes
                   ;;   that there is no danger of underflow and that the
                   ;;   values are unsigned.
                   ;;
                   
2000: 78 81                mov      R0, #81h        ; value 1
2002: 79 23                 mov     R1, #23h
                            
2004: 7C 83                 mov     R4, #83h        ; value 2
2006: 7D 10                 mov     R5, #10h
                   
2008: C3                    clr     C               ; Don't know what the carry
                            
2009: E8                    mov     A, R0
200A: 99                    subb    A, R1           ; A now holds FEh and the C
                            
200B: FE                    mov     R6, A           ; MOV instructions do not c
                            
200C: EC                    mov     A, R4
200D: 9D                    subb    A, R5           ; This subtracts 10h from 2
                                                    ;   A now contains 12h
200E: FF                    mov     R7, A
                            
                   ;;
                   ;; R7/R6 now holds 12FEhh
                   ;;
                            
200F: 22                   ret
                   
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;; Data                                      ;;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Multiplication

OK, that was the easy stuff. Next is not super complicated, but a lot of details. First thing to remember is that the size of the product will be the sume of the sizes of the numbers being multiplied together. Since we are multiplying two 16-bit numbers, the size will be 32-bits. (Yes, I know that multiplying 1 times 1 can fit in a smaller sized container, we are only worried about the worst case since we are stuck in that size, unless we re-write the code!) Let's work one so you can walk through an example in little steps.

Let's multiple 4721h by 5F2Ah. Seems simple enough. Now we have to work with bytes. For ease of reading, I will put a space between the two bytes.

      47 21
      5F 2A
      -----
?? ?? ?? ??
  
That looks pretty! Now we start by multiplying 21 by 2A:
      47 21
      5F 2A
      -----
      05 6A
?? ?? ?? ??
  
Now for step 2: We multiply 47 by 2A, but shift the answer to the left by one byte:
      47 21
      5F 2A
      -----
      05 6A
   0B A6
?? ?? ?? ??
  
For the third step, we multiple 21 by 5F (Note that we do not shift the answer this time!):
      47 21
      5F 2A
      -----
      05 6A
   0B A6
   0C 3F
?? ?? ?? ??
  
One more time, we multiple 47 by 5F and shift:
      47 21
      5F 2A
      -----
      05 6A
   0B A6
   0C 3F
1A 59
-----------
?? ?? ?? ??
  
Now all that is left is to add up the values, a column at a time:
      47 21
      5F 2A
      -----
      05 6A
   0B A6
   0C 3F
1A 59
-----------
?? ?? ?? 6A
  
OK, again:
      47 21
      5F 2A
      -----
      05 6A
   0B A6
   0C 3F
1A 59
-----------
?? ?? EA 6A
  
Third column:
      47 21
      5F 2A
      -----
      05 6A
   0B A6
   0C 3F
1A 59
-----------
?? 70 EA 6A
  
And, finally:
      47 21
      5F 2A
      -----
      05 6A
   0B A6
   0C 3F
1A 59
-----------
1A 70 EA 6A
  

Make the example

The reason for such small steps is not to make it easier to understand, which is a good reason by itself, but to make it easier to write the code to do it! We will also look at this problem with a couple of new goals in mind. First, we will want make this into a subroutine (this is a good example of when to use one), which means that we want to minimize the impact on other code. We will also need three storage areas, two 16-bit areas for the numbers we want to multiple together and one 32-bit area for the answer. Remember, we have to code for worst case! Add it up, that comes to 8 bytes. Also, we want to make this as fast as possible, so we have to use the Rn registers for this.

We will use Register bank 3 for with its 8 one-byte registers for this. We will allocate the registers in the following order:

R0 Least significant byte number 1
R1 Most significant byte
R2 Least significant byte number 2
R3 Most significant byte
R4 bits 0 - 7 product
R5 bits 8 - 15
R6 bits 16 - 23
R7 bits 24 - 31

Next will we define the interface for the programmer to use this function.

  1. The programmer must put number 1 and number 2 into the proper registers, in whatever manner is appropriate for the task.
  2. There is no requirement that Bank 3 be the active bank.
  3. The programmer must get the results from the registers after the multiplication.

Example

2000:                   .org    0x2000
                   
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;; Equates                                  ;;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   
                   
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;; Code                                      ;;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   
                   begin:
                   ;;
                   ;; Multiple 5F23h by 4422h.
                   ;;
                   start:
2000: 75 18 23             mov     18h, #23h       ; number 1
2003: 75 19 5F             mov     19h, #5Fh
                   
2006: 75 1A 22             mov     1Ah, #22h       ; number 2
2009: 75 1B 44             mov     1Bh, #44h
                   
200C: 12 20 10             lcall   mul16           ;; do it!  That is simple!
                                                   ;; However we will just
                                                   ;; ignore the result in thi
                                                   ;; example.
                   
200F: 22                   ret
                   
                   ;;
                   ;; 16-bit multiplication function
                   ;;
                   
                   mul16:
2010: D2 D3                setb    RS0     ; Since we don't know what the acti
2012: D2 D4                setb    RS1     ; bank is, we will force Bank 3 act
                   
2014: E8                   mov     A, R0   
2015: 8A F0                mov     B, R2
2017: A4                   mul     AB
2018: FC                   mov     R4, A
2019: AD F0                mov     R5, B
                   
201B: E9                   mov     A, R1
201C: 8A F0                mov     B, R2
201E: A4                   mul     AB
201F: 2D                   add     A, R5    ; This is not using ADDC!
2020: FD                   mov     R5, A    ; ADD only puts the answer in the A
2021: AE F0                mov     R6, B
                   
2023: EB                   mov     A, R3
2024: 88 F0                mov     B, R0
2026: A4                   mul     AB
2027: 2D                   add     A, R5    ; Make sure to save the LSB first!!
2028: FD                   mov     R5, A
2029: E5 F0                mov     A, B     ; The ADD instruction does not wor
202B: 2E                   add     A, R6    ; once again, carry is ignored.
                   
202C: EB                   mov     A, R3
202D: 89 F0                mov     B, R1
202F: A4                   mul     AB
2030: 2E                   add     A, R6
2031: FE                   mov     R6, A
2032: AF F0                mov     R7, B
                                            
2034: 22                   ret
                   
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;; Data                                      ;;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Division

The coward's way out is to make a function for 16-bit subtraction, and then set up a loop and call the function the proper number of times. Your first project will point you to a web page where there is a pure 16-bit division function for you to use on that project.


©2004, Gary L. Burt