Model Boat Mayhem

Please login or register.

Login with username, password and session length.
Pages: 1 2 3 4 5 6 [7]   Go Down

Author Topic: Help writing pic code  (Read 22854 times)


  • Full Mayhemer
  • *****
  • Offline Offline
  • Posts: 113
  • Location: Newcastle upon Tyne, England
Re: Help writing pic code
« Reply #150 on: December 04, 2014, 01:42:26 PM »

Hi Stew

No, I've never used any PICs other than the 16F's. Many years ago I started teacvhing myself C, and I liked that language compared to the Turbobasic I'd used previously. But, now I like the assembler coding as I feel close to the action and, so far, these PICs can do more than I need.

Below is the code I use for multiplying. As it says, it handles two 16-bit values (signed or unsigned) and gives a 32-bit result.
I'll sort out the division and subtraction code in a while.

I initially obtained these routines at and they ask to link to their site rather than copy. Trouble is, my routines have been modified from the original and I know my code works fine.

One other point to bear in mind. I use PICs that have the extended mid-trange instruction set. I cannot remeber if any of the extended instructions are used in these routines.


; Combined sub-routines to multiply 2 16-bit numbers to a 32-bit product.

;  registers used: 
;                    product(4 bytes)
;                    m_plier(2 bytes)
;                    m_plicand(2 bytes)
;                    bitcount
; flag register:
;             misc_flags bit:    MULT_NEG    ; setting this bit signifies m_plier was negative

;            Call subroutine starting here when m_plier is signed.

;    Test sign of m_plier and reverse if negative.

            BANKSEL        m_plier
            btfss        m_plier+1, 7        ; is sign bit set?       
            goto        multiply_16to32

            comf        m_plier, F            ; sign of m_plier is -ve so make 2's compl
            comf        m_plier+1, F
            incfsz        m_plier, F                ; If result of incr is not zero, then no need to adjust high byte
            goto        _set_flag
            incf        m_plier+1, F
            BANKSEL        misc_flags
            bsf            misc_flags, MULT_NEG            ; m_plier negative so set flag
            goto        _clear_product

;            Call subroutine starting here when m_plier is unsigned.

            BANKSEL        misc_flags
            bcf            misc_flags, MULT_NEG            ; ensure flag shows result is +ve

            BANKSEL        product
            CLRF        product
            CLRF        product+1
            CLRF        product+2
            CLRF        product+3
            BANKSEL        bitcount
            MOVLW        d'16'            ; 16 bit operations
            MOVWF        bitcount

            BANKSEL        m_plier
            RRF            m_plier+1, F    ; shift both bytes of multiplier down
            RRF            m_plier, F
            BTFSS        STATUS, C        ; If carry is set, then least sig bit was 1 so add current value of multiplicand to product
            GOTO        _mult32_2            ; carry not set so skip addition

            BANKSEL        product+4
            CLRF        product+4
            BANKSEL        m_plicand        ; start 16-bit addition
            MOVF        m_plicand+1, W
            BANKSEL        product
            ADDWF        product+3, F
            BTFSC        STATUS, C        ; check if a carry to byte-4 is needed
            INCF        product+4, F

            BANKSEL        m_plicand
            MOVF        m_plicand, W
            BANKSEL        product
            ADDWF        product+2, F
            BTFSC        STATUS, C        ; check if a carry to byte-1 is needed
            INCFSZ        product+3, F
            GOTO        $+2
            INCF        product+4, F           
            BCF            STATUS, C        ; ensure carry bit is clear, then rotate m_plicand bytes upwards
            BANKSEL        product        ; rotate low byte first so any carry is accomplished automatically.
            RRF            product+4, F
            RRF            product+3, F
            RRF            product+2, F
            RRF            product+1, F
            RRF            product, F

            BANKSEL        bitcount
            DECFSZ        bitcount, F
            GOTO        _mult32_1
;            Lastly test to see if result must be converted to 2's complement

            BANKSEL        misc_flags
            btfss        misc_flags, MULT_NEG
            BANKSEL        product
            comf        product, F
            comf        product+1, F
            comf        product+2, F
            comf        product+3, F

            incfsz        product, F
            incfsz        product+1, F
            incfsz        product+2, F
            incf        product+3, F


  • Full Mayhemer
  • *****
  • Offline Offline
  • Posts: 113
  • Location: Newcastle upon Tyne, England
Re: Help writing pic code
« Reply #151 on: December 04, 2014, 02:59:48 PM »

Hi again

Here is the divide subroutine. If you only need to divide a 16-bit number then you just set the unwanted upper byte to zero. No doubt you'd get code that is a bit shorter and faster if it is specific for 16 bit by 16bit division, but I just use this one routine to keep life simple(r).

By the way, there are various ways in use top name the bytes in multi-byte variables. Some people define a different name for each register. I generally define one name for all the registers in a CBLOCK directive such as:

    CBLOCK    0x20
# insert here other variables


This gives makes the name "dividend" refer to the first byte and the value 3 after the colon makes the assembler reserve a total of 3 bytes before assigning the next address to a new name.

Then, one has to be consistent about whether the first byte is the least or the most significant. In these routines I always use the first byte as the LSB. Then, if, for example, I wanted to move the lowest and then the highest bytes of the dividend to W register, I'd write:

movf  dividend, W

movf  dividend+2, W

The assembler adds 2 to the value of the address assigned to "dividend" and places the result of that addition in the machine code that defines the address to be read.

Its crucial to get the byte order correct.


; Sub-routine to divide a 24-bit number by a 16-bit number to give a 24-bit quotient.
; This routine was written by Nikolai Golovchenko (
;    Names of registers have been changed to make it easier to follow etc.
;    Registers: dividend (3 bytes), divisor (2 bytes)
;    Registers: remainder (2 bytes), bitcount (1 byte)


            BANKSEL        remainder
            CLRF        remainder
            CLRF        remainder+1
            MOVLW        d'24'
            MOVWF        bitcount
            RLF            dividend, F
            RLF            dividend+1, F
            RLF            dividend+2, F
            BANKSEL        remainder
            RLF            remainder, F            ;    shift carry into remainder
            RLF            remainder+1, F
            BANKSEL        bitcount
            RLF            bitcount, F                ; save carry in bitcount

            MOVF        divisor, W                ;  subtract divisor from remainder
            BANKSEL        remainder
            SUBWF        remainder, F   
            BANKSEL        divisor
            MOVF        divisor+1, W
            BTFSS        STATUS, C
            INCFSZ        divisor+1, W
            SUBWF        remainder+1, W            ; keep that byte in W until we are sure about the borrow.

            BTFSC        STATUS, C                ; if no borrow
            BSF            bitcount, 0                ; set bit zero of bit counter (saved carry)

            BTFSC        bitcount, 0                ; if no borrow
            GOTO        _div_2

            MOVF        divisor, W                ; restore remainder if borrow
            ADDWF        remainder, F
            MOVF        remainder+1, W            ; read high byte of remainder to W,
                                                ; to not change it by next instruction
            MOVWF        remainder+1                ; store high byte of remainder
            BCF            STATUS, C                ; clear carry
            RRF            bitcount, F                ; and restore counter

            DECFSZ        bitcount, F                ; decrement bit counter
            GOTO        _div_1                    ; repeat loop if not zero

            RLF            dividend, F                ;  shift in last bit of result
            RLF            dividend+1, F
            RLF            dividend+2, F           



  • Full Mayhemer
  • *****
  • Offline Offline
  • Posts: 113
  • Location: Newcastle upon Tyne, England
Re: Help writing pic code
« Reply #152 on: December 04, 2014, 03:35:49 PM »

Hi Stew

Last offering of code. I know all four of these maths procedures work but I wouldn't be at all surprised if Ian comes up with some better versions.

I don't use subroutines for subtraction or addition. Here are the blocks of code that do the jobs.

I have put in some dummy names into this section. This code adds 16-bit varA to 16-bit varB, result in varB
          BANKSEL        varA
            movf            varA, W
            addwf            varB, F
            btfsc            STATUS, C                ;     If carry set then increment upper byte
            incf            varB+1, F
            movf            varA+1, W
            addwf            varB+1, F

The following code subtracts 16-bit constant defined as MAX_FSPEED_MSB / MAX_SPEED_LSB  from 16-bit variable "Fpump_speed" with the result placed in 16-bit variable "temp". The result of this is a signed value. i.e. if the result is negative, then bit 7 of temp+1 =1, otherise it is 0.

            movlw            MAX_FSPEED_MSB        ;    Place msb in temp+1
            movwf            temp+1
            movlw            MAX_FSPEED_LSB            ;    Place lsb in temp
            movwf            temp
            movf            Fpump_speed, W            ;      subtract lsb from max value
            subwf            temp, F
            movf            Fpump_speed+1, W        ;      subtract msb from max value
            btfss            STATUS, C
            incfsz            Fpump_speed+1, W
            subwf            temp+1, F


  • Full Mayhemer
  • *****
  • Offline Offline
  • Posts: 70
  • Location: North Shields
Re: Help writing pic code
« Reply #153 on: December 04, 2014, 05:14:03 PM »

Hi Mike
Thanks for those routines. I'll look through them and try to figure out what's going on.
In the mean time I'll stick to using 8bit numbers.
I've looked on the piclist website you mentioned and they have a lot of useful routines but they're not half complicated.
Pages: 1 2 3 4 5 6 [7]   Go Up