Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

16-bit multiplication in Assembly

Peet19

Member
Hi everyone!
I would like to ask for a little help again.
I want to multiply the values of the ADRESH and ADRESL registers by 10. How can this be solved?
There is no problem with 8-bit multiplication, but I don't know how to do it with 16-bit.
Thank you in advance for your help!
In Assembly, of course.
 
Thank's I look.
I forgot to say that the maximum binary value that must be multiplied by 10 is 614. So the result will definitely fit in the 16-bit PRODH and PRODL registers.
 
I would be suprised if there was not one to do it there, or at least adapt one if necessary.
The Piclist is a good resource.
If you need delays, BTW, this guy was there for a long time, but now has his own site.
 
multiply by 10 in assembler is easy (x <<1) + (x<<3) but your routine will need to shift MSbit into LSBit of the high byte...
Code:
256 << 1 = 512       00000001 00000000 = 00000010 00000000
256 << 3 = 2048      00000001 00000000 = 00001000 00000000
2048 + 512 = 2560                      = 00001010 00000000
 
Here's a little something I used in my simple BASIC compiler, this is the math.inc file, and simply uses 16 bit integer maths - there are three 16 bit accumulators, Acc1L/H, Acc2L/H and Acc3L/H - the input values are set in two of them, the routine called, and the result returned in a third.

Code:
    NOLIST
_PINZ        ADDWF PCL
    RETLW d'1'
    RETLW d'2'
    RETLW d'4'
    RETLW d'8'
    RETLW d'16'
    RETLW d'32'
    RETLW d'64'
    RETLW d'128'
_Sub16       COMF   Acc2H       ; Take two's complement
    COMF   Acc2L       ; of Acc2. If zero, Acc2L
    INCF   Acc2L
    BTFSC  STATUS, Z   ; overflowed, so carry
    INCF   Acc2H       ; into Acc2H.
_Add16       MOVF   Acc2L, w    ; Add the LSBs. If carry,
    ADDWF  Acc1L
    BTFSC  STATUS, C   ; increment MSB of sum.
    INCF   Acc1H
    MOVF   Acc2H, w    ; Add the MSBs
    ADDWF  Acc1H
    MOVF   Acc1L, w    ; return lobyte in W
    RETURN
_Mult16
    CLRF   Acc3H          ; Clear product to make
    CLRF   Acc3L         ; way for new calculation.
    MOVLW  d'16'        ; Number of bits to calc.
    MOVWF  TEMP1
Multiply_loop
    BCF    STATUS, C
    RLF    Acc3L         ; Shift product left.
    RLF    Acc3H
    BCF    STATUS, C
    RLF    Acc1L        ; Shift multiplicand left.
    RLF    Acc1H
    BTFSS  STATUS, C    ; If carry, add multiplier
    GOTO   Multiply_skip
    MOVF   Acc2L,w      ; to the product. Else skip.
    ADDWF  Acc3L
    BTFSC  STATUS, C    ; 16-bit addition: prod+mulp
    INCF   Acc3H
    MOVF   Acc2H, w
    ADDWF  Acc3H
Multiply_skip
    DECFSZ TEMP1
    GOTO   Multiply_loop
    MOVF   Acc3H, w
    MOVWF  Acc1H        ; return answer in Acc1
    MOVF   Acc3L, w
    RETURN
_DIV16          MOVF    Acc2H, w                ; Check for division by 0.
    IORWF   Acc2L, w
    BTFSC   STATUS, Z
    RETLW   d'255'        ; Error code= 255: return.
    MOVLW   d'1'          ; Otherwise, initialize variables
    MOVWF   TEMP1
    CLRF    TEMP2         ; index for the division.
    CLRF    Acc3H
    CLRF    Acc3L
Divide_sh_loop  BTFSC   Acc2H, d'7'   ; Shift divisor left
    GOTO    Divide_d1
    BCF     STATUS, C     ; until msb is in
    RLF     Acc2L         ; Acc2H.7.
    RLF     Acc2H         ; TEMP1 = no. of shifts+1.
    INCF    TEMP1
    GOTO    Divide_sh_loop
Divide_d1       BCF     STATUS, C
    RLF     Acc3L          ; Shift quotient left.
    RLF     Acc3H
    MOVF    Acc2L,w       ; top = top - btm.
    SUBWF   Acc1L
    BTFSC   STATUS, C     ; If top - btm < 0 then
    GOTO    Divide_d2
    MOVLW   d'1'          ; top = top + btm
    SUBWF   Acc1H
    BTFSC   STATUS, C     ; The idea is to do the
    GOTO    Divide_d2
    INCF    Acc1H         ; the subtraction and comparison
    MOVF    Acc2L, w      ; (top > btm?) in one step.
    ADDWF   Acc1L
    goto    Divide_reentr ; Then, if btm > top, undo <Microchip instruction>
Divide_d2       MOVF    Acc2H, w      ; the subtraction by adding
    SUBWF   Acc1H
    BTFSS   STATUS, C     ; top and btm back together
    goto    Divide_less   ; <Microchip instruction>
    BSF     Acc3L,  d'0'
Divide_reentr
    BCF     STATUS, C
    RRF     Acc2H
    RRF     Acc2L
    DECFSZ  TEMP1
    GOTO    Divide_d1
    MOVF    Acc3H, w
    MOVWF   Acc1H         ; return answer in Acc1
    MOVF    Acc3L, w
    MOVWF   Acc1L         ; with lobyte in W
    RETURN
Divide_less     MOVF    Acc2L, w      ; btm > top, so
    ADDWF   Acc1L
    BTFSC   STATUS, C     ; undo the subtraction by
    INCF    Acc1H         ; adding them back together.
    MOVF    Acc2H, w
    ADDWF   Acc1H
    goto    Divide_reentr ; <Microchip instruction>
_SHIFTR         MOVWF    Acc2L
_LoopR          BCF      STATUS, C
    RRF      Acc1H, F
    RRF      Acc1L, F
    DECFSZ   Acc2L
    GOTO     _LoopR
    MOVF     Acc1L, W
    RETURN
_SHIFTL         MOVWF    Acc2L
_LoopL          BCF      STATUS, C
    RLF      Acc1L, F
    RLF      Acc1H, F
    DECFSZ   Acc2L
    GOTO     _LoopL
    MOVF     Acc1L, W
    RETURN
_CONVERT:                       ; Takes number in Acc1H:Acc1L
    ; Returns decimal in
    ; TenK:Thou:Hund:Tens:Ones
    swapf   Acc1H, w
    iorlw    B'11110000'
    movwf   Thou
    addwf   Thou,f
    addlw   0XE2
    movwf   Hund
    addlw   0X32
    movwf   Ones
    movf    Acc1H,w
    andlw   0X0F
    addwf   Hund,f
    addwf   Hund,f
    addwf   Ones,f
    addlw   0XE9
    movwf   Tens
    addwf   Tens,f
    addwf   Tens,f
    swapf   Acc1L,w
    andlw   0X0F
    addwf   Tens,f
    addwf   Ones,f
    rlf     Tens,f
    rlf     Ones,f
    comf    Ones,f
    rlf     Ones,f
    movf    Acc1L,w
    andlw   0X0F
    addwf   Ones,f
    rlf     Thou,f
    movlw   0X07
    movwf   TenK
    movlw   0X0A
Lb1:
    addwf   Ones,f
    decf    Tens,f
    btfss   3,0
    goto   Lb1
Lb2:
    addwf   Tens,f
    decf    Hund,f
    btfss   3,0
    goto   Lb2
Lb3:
    addwf   Hund,f
    decf    Thou,f
    btfss   3,0
    goto   Lb3
Lb4:
    addwf   Thou,f
    decf    TenK,f
    btfss   3,0
    goto   Lb4
    return
_COMP16:
    movf    Acc1H, w
    subwf   Acc2H, w
    btfss   STATUS, Z
    goto    $+3
    movf    Acc1L, w
    subwf   Acc2L,
    return
_COMP16I:
    movf    Acc2H, w
    subwf   Acc1H, w
    btfss   STATUS, Z
    goto    $+3
    movf    Acc2L, w
    subwf   Acc1L, w
    return
    LIST
 
Nigel:
Thanks, looks good. I haven't tried it yet, but I'm reading it. Good length :)
Anyway, I managed to solve it, although I only concentrated on the 10x multiplication. In addition, I know in advance that the result will never be larger than 16 bits.

Thanks Ian, very good algorithm.
 
Nigel:
Thanks, looks good. I haven't tried it yet, but I'm reading it. Good length :)

Very little of the code, if any, is mine :D

It either came from the PICLIST, or it might have been from "The PIC Source Book", it was a very long time ago :D

It was one of a small number of include files which my BASIC interpreter included, if required - the entire resulting code was then fed to MPASM for assembly.
 
You didn't mention which pic, but since you mentioned the PRODH/PRODL registers I assume it's a PIC18
Here's one using the hardware MUL
Code:
    MOVF ADRESH,W,0
    MULLW 10
    MOVFF PRODL,WRESULTH    ; word result high byte
    MOVF ADRESL,W,0
    MULLW 10
    MOVFF PRODL,WRESULTL    ; word result low byte
    MOVF PRODH,W,0
    ADDWF WRESULTH,F,0
It assumes everything's in the access bank.
 

Latest threads

New Articles From Microcontroller Tips

Back
Top