## Multi-byte Addition & Subtraction

This page was originally published by me on a site called pic-projects.net.

The Microchip PIC instruction set provides opcodes that will perform the addition or subtraction two 8-bit values but what if we need to add or subtract 16-, 24- or 32-bit values held in several bytes?

The following code illustrates how to perform 16-, 24- and 32-bit arithmetic on values held in multiple registers. The examples use a number of register areas with prefixes 'R', 'A' and 'B'. The input values to an arithmetic operation will either be in the 'R' and 'A' registers (for R += A and R -= A) or the 'A' and 'B' registers (for R = A + B and R = A - B). The result of the operation is always placed in the 'R' registers.

The number of 'R', 'A' and 'B' registers used depends on size of the values. A 16-bit operation will only use registers suffixed with a 0 or a 1 (e.g. R0 and R1), while a 24-bit operation will use 0, 1, 2 and a 32-bit operation will use 0, 1, 2 and 3. The bigger the suffix value, the more significant the byte it holds.

The all of the code assumes that A0-3, B0-3 and R0-3 are accessible simultaneously. This means that they must either be in the same bank of file registers or split between a single bank and the common shared (or access) file register area.

### Addition

The ADDWF instruction provided on all PIC devices adds two 8-bit values but does not take into account the value of the carry flag in the STATUS register. Although the carry is not used in the calculation it will be set afterwards to indicate if the result was larger than 8-bits (e.g. h'c0' + h'40' = h'00' and C=1).

The following code implements addition for various size values held in registers A0-3 with the values in R0-3 leaving the result in R0-3 (e.g. R += A).

```; 16-bit Addition (All Devices)
movf    A0,W
addwf   R0,F
movf    A1,W
btfsc   STATUS,C
incfsz  A1,W
addwf   R1,F

; 24-bit Addition (All Devices)
movf    A0,W
addwf   R0,F
movf    A1,W
btfsc   STATUS,C
incfsz  A1,W
addwf   R1,F
movf    A2,W
btfsc   STATUS,C
incfsz  A2,W
addwf   R2,F

; 32-bit Addition (All devices)
movf    A0,W
addwf   R0,F
movf    A1,W
btfsc   STATUS,C
incfsz  A1,W
addwf   R1,F
movf    A2,W
btfsc   STATUS,C
incfsz  A2,W
addwf   R2,F
movf    A3,W
btfsc   STATUS,C
incfsz  A3,W
addwf   R3,F```

To understand how this code propagates the carry into the calculation let's break down series of instructions used for all the bytes other than the first pair.

1. `movf Ax,W`

This instruction simply copies the next Ax byte to be added into WREG. The MOVF instruction does not affect the carry bit generated from the last addition.

2. `btfsc STATUS,C`

This tests to see if a carry was generated by the previous stage of the addition. If there wasn't a carry then we can skip directly to the addition in step (4).

3. `incfsz Ax,W`

If there was a carry then we need to add Ax + 1 to Rx instead of just Ax but if Ax was h'ff' then incrementing it will cause it to overflow leaving h'00' in WREG. When this happens the there is no need perform an addition with Rx (i.e. Rx + h'00' = Rx) but we must pass the carry flag, which is unaffected by the INCFSZ instruction, on to the next stage by skipping step (4).

4. `addwf Rx,W`

This performs the addition of either Ax (when there was no carry) or Ax + 1 (if there was a carry but Ax wasn't h'ff') to Rx.

If we wanted to add two values and store the result in a different set of file registers (e.g. R = A + B) then the easiest way is to combine a value copy (e.g. R = A) and with the addition (e.g (R = A) += B).

```; 16-bit Addition (All Devices)
movf    B0,W
addwf   A0,W
movwf   R0
movf    A1,W
movwf   R1
movf    B1,W
btfsc   STATUS,C
incfsz  B1,W
addwf   R1,F

; 24-bit Addition (All Devices)
movf    B0,W
addwf   A0,W
movwf   R0
movf    A1,W
movwf   R1
movf    B1,W
btfsc   STATUS,C
incfsz  B1,W
addwf   R1,F
movf    A2,W
movwf   R2
movf    B2,W
btfsc   STATUS,C
incfsz  B2,W
addwf   R2,F

; 32-bit Addition (All devices)
movf    B0,W
addwf   A0,W
movwf   R0
movf    A1,W
movwf   R1
movf    B1,W
btfsc   STATUS,C
incfsz  B1,W
addwf   R1,F
movf    A2,W
movwf   R2
movf    B2,W
btfsc   STATUS,C
incfsz  B2,W
addwf   R2,F
movf    A3,W
movwf   R3
movf    B3,W
btfsc   STATUS,C
incfsz  B3,W
addwf   R3,F```

The enhanced mid-range and high performance devices have an ADDWFC instruction that does include the carry flag in the calculation, making the code sequences simpler and faster.

```; 16-bit Addition (Enhanced Mid-Range and High Performance devices)
movf    A0,W
addwf   R0,F
movf    A1,W
addwfc  R1,F

; 24-bit Addition (Enhanced Mid-Range and High Performance devices)
movf    A0,W
addwf   R0,F
movf    A1,W
addwfc  R1,F
movf    A2,W
addwfc  R2,F

; 32-bit Addition (Enhanced Mid-Range and High Performance devices)
movf    A0,W
addwf   R0,F
movf    A1,W
addwfc  R1,F
movf    A2,W
addwfc  R2,F
movf    A3,W
addwfc  R3,F```

This also makes it much easier to handle the case where the result of the addition is stored in a separate area.

```; 16-bit Addition (Enhanced Mid-Range and High Performance devices)
movf    B0,W
addwf   A0,W
movwf   R0
movf    B1,W
addwfc  A1,W
movwf   R1

; 24-bit Addition (Enhanced Mid-Range and High Performance devices)
movf    B0,W
addwf   A0,W
movwf   R0
movf    B1,W
addwfc  A1,W
movwf   R1
movf    B2,W
addwfc  A2,W
movwf   R2

; 32-bit Addition (Enhanced Mid-Range and High Performance devices)
movf    B0,W
addwf   A0,W
movwf   R0
movf    B1,W
addwfc  A1,W
movwf   R1
movf    B2,W
addwfc  A2,W
movwf   R2
movf    B3,W
addwfc  A3,F
movwf   R3```

### Subtraction

It should come as no surprise that a device that can't propagate a carry during addition, can't propagate a borrow during subtraction either. Fortunately the same approach of using a sequence of conditional instructions to adjust the data values can be applied.

The following code implements subtraction for various size values held in registers A0-3 with the values in R0-3 leaving the result in R0-3 (e.g. R -= A).

```; 16-bit Subtraction (All Devices)
movf    A0,W
subwf   R0,F
movf    A1,W
btfss   STATUS,C
incfsz  A1,W
subwf   B1,F

; 24-bit Subtraction (All Devices)
movf    A0,W
subwf   R0,F
movf    A1,W
btfss   STATUS,C
incfsz  A1,W
subwf   R1,F
movf    A2,W
btfss   STATUS,C
incfsz  A2,W
subwf   R2,F

; 32-bit Subtraction (All devices)
movf    A0,W
subwf   R0,F
movf    A1,W
btfss   STATUS,C
incfsz  A1,W
subwf   R1,F
movf    A2,W
btfss   STATUS,C
incfsz  A2,W
subwf   R2,F
movf    A3,W
btfss   STATUS,C
incfsz  A3,W
subwf   R3,F```

Again it is a re-occurring pattern of four instructions that handle the propagation of the borrow from one byte to the next during the calculation.

1. `movf Ax,W`

As in the addition pattern this instruction simply loads WREG with the next byte value that is going to be subtracted.

2. `btfss STATUS,C`

This instruction tests if the last stage of the subtraction generated a borrow (e.g. C == 0). If there is no borrow to be performed then we can skip directly to the subtraction in step (4).

3. `incfsz Ax,W`

If there is a borrow to be accounted for then we must subtract Ax + 1 from Rx instead of just Ax, but as with addition if Ax is h'ff' before the increment then it will overflow becoming h'00'. In this case there is no need to perform a subtraction from Rx, because we'd be subtracting zero, but we much pass the borrow on to the next stage instead, skipping step (4)

4. `subwf Rx,F`

This instruction subtracts either Ax (if there was no borrow) or Ax + 1 (if there was a borrow but Ax didn't overflow) held in WREG from Rx to generate the next result byte. If WREG contains a bigger value than Rx then a borrow will be generated for the next stage.

As with addition we can combine the subtraction with a value transfer to create a sequence for R = A - B

```; 16-bit Subtract (All Devices)
movf    B0,W
subwf   A0,W
movwf   R0
movf    A1,W
movwf   R1
movf    B1,W
btfss   STATUS,C
incfsz  B1,W
subwf   R1,F```

Again enhanced mid-range and high performance processors have a additional instruction, SUBWFB, that propagates the borrow as part of its operation making subtraction more efficient on these devices.

```; 16-bit Subtraction (Enhanced Mid-Range and High Performance devices)
movf    A0,W
subwf   R0,F
movf    A1,W
subwfb  R1,F

; 24-bit Subtraction (Enhanced Mid-Range and High Performance devices)
movf    A0,W
subwf   R0,F
movf    A1,W
subwfb  R1,F
movf    A2,W
subwfb  R2,F

; 32-bit Subtraction (Enhanced Mid-Range and High Performance devices)
movf    A0,W
subwf   R0,F
movf    A1,W
subwfb  R1,F
movf    A2,W
subwfb  R2,F
movf    A3,W
subwfb  R3,F```

And as with addition the sequences for R = A - B are also much simpler.

```; 16-bit Subtraction (Enhanced Mid-Range and High Performance devices)
movf    B0,W
subwf   A0,W
movwf   R0
movf    B1,W
subwfb  A1,W
movwf   R1

; 24-bit Subtraction (Enhanced Mid-Range and High Performance devices)
movf    B0,W
subwf   A0,W
movwf   R0
movf    B1,W
subwfb  A1,W
movwf   R1
movf    B2,W
subwfb  A2,W
movwf   R2

; 32-bit Subtraction (Enhanced Mid-Range and High Performance devices)
movf    B0,W
subwf   A0,W
movwf   R0
movf    B1,W
subwfb  A1,W
movwf   R1
movf    B2,W
subwfb  A2,W
movwf   R2
movf    B3,W
subwfb  A3,W
movwf   R3```

This page was last updated on 2nd February 2018