Learning to program the PIC16LF1823

MaxHeadRoom

Joined Jul 18, 2013
30,665
I have always done it by the 'book'
movlw b'00000000' ;MFINTOSC (32 KHz with PLL enabled)
movwf OSCCON
Are you pressed for memory?
I don't think you will have any error trying it your way.
Same thing.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
I have always done it by the 'book'
movlw b'00000010' ;MFINTOSC (32 KHz with PLL enabled)
movwf OSCCON
Are you pressed for memory?
I don't think you will have any error trying it your way.
No, I'm not really pressed on memory. The program I'm writing is extremely simple and I'll only be using two or three GP memory registers.

Thanks for your help.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
Here's my latest conundrum:

I'm using FSR1 to read a table in memory in which a set of ascii characters is stored. To accomplish this, I'm using the following routine:

Code:
      MovLf low  Target_00_HB, FSR1L
      MovLf high Target_00_HB, FSR1H
     
      MovLf d'64', CNT00      ;load the total received first 64 bytes (16 first BCD two-byte registers)
Store_Mod_Table_Loop_0:
        moviw FSR0++          ;get the first character in the pair
        addlw nDECIMAL_CONV   ;add it with nDECIMAL_CONV
        movwf NUMBER          ;NUMBER now contains the converted BCD
        swapf NUMBER, f       ;place the result in the upper nibble      
        moviw FSR0++          ;get the second character in the pair
        addlw nDECIMAL_CONV   ;add it to nDECIMAL_CONV, this is now the lower nibble
        iorwf NUMBER, w       ;merge it with the previously received and converted high BCD, and store it into w
        movwi FSR1++          ;store the result in the modulation table
      decfsz CNT00, f
     goto Store_Mod_Table_Loop_0
The previous code, reads 64 sets of two ascii characters, converts them into a single BCD byte, and stores said bytes at an address pointed at by FSR1. Said address starts at the location of Target_00_HB, and then increments as the loop is executed.

The previous code works beautifully, and I have no complaints about it.

However, if I continue said code with the following one:

Code:
      Fcall Wait_1_10_s
      TransGPRSStr ATpFTPGET_324_start ;bank 0, download the remaining batch of data
     
      ;purge the expected 18 characters from the first part of the response: "\r\n+FTPGET: 2,324\r\n"
      MovLf d'18', CNT00
Purge_324_Loop:
       Fcall Receive_W_TO     ;returns with active bank 0
      decfsz CNT00, f
     goto Purge_324_Loop
     
      MovLf d'160', CNT00    
Store_Mod_Table_Loop_1:
       Fcall Receive_BCD           ;get a pair of characters directly from the UART and build a BCD
       movwi FSR1++                ;store the result in the modulation table
      decfsz CNT00, f
     goto Store_Mod_Table_Loop_1
The results calculated in Store_Mod_Table_Loop_1 are not stored at the proper location supposedly pointed at by FSR1. This location should be the continuation of the value FSR1 had at the end of the first part of the code. I've gone through every single step of this last bit of code, and there's not a single instruction in which FSR1 is supposed to change. And nevertheless it seems to get corrupted.

So what I did was, immediately after executing Store_Mod_Table_Loop_0, I backed up the value held by FSR1:
Code:
     banksel BACKUP0         ;bank 1
     MovF2F FSR1L, BACKUP0
     MovF2F FSR1H, BACKUP1
And then I restored it immediately before executing Store_Mod_Table_Loop_1:
Code:
     banksel BACKUP0         ;bank 1
     MovF2F BACKUP0, FSR1L
     MovF2F BACKUP1, FSR1H

BTW, the MovF2F instruction is simply the following macro:
Code:
;Move File to File
MovF2F macro FileSource, FileDest   ;Move a file into another file, using w as a mediator
         movf FileSource, w
         movwf FileDest
       endm
I use this macro extensively throughout my code and it works perfectly fine in all cases.

Anyway, backing up and then restoring the value of FSR1 using the BACKUPx registers did not work either! ... FSR1 is still not even remotely pointing at the address it is supposed to point at, what gives?
 

joeyd999

Joined Jun 6, 2011
6,304
There is no such instruction in the pic16lf1825 ... (although I'm already familiar with it in the 8051 architecture) ... is it some sort of macro?
Here's a binary to BCD conversion routine I wrote a little over 100 years ago. You can extract out the BCD addition code where the BCD accumulator is doubled by summing temp2:0 (a copy of bcd2:0) into bcd2:0.

Code:
;*******  Convert Raw 24 Bit Number in bin2:0 to packed BCD in bcd2:0 **********

;must define two carry flags cy0 and cy1
;i.e:
;#define cy0    flags,0
;#define cy1    flags,1
      
binbcd    clrf    bcd2        ;ready for BCD conversion
    clrf    bcd1        ;clear BCD accumulators
    clrf    bcd0
    movlw    24        ;24 bits worth of processing
    movwf    bitcnt        ;one bit at a time

bloop    movfw    bcd2        ;prepare to double BCD number in
    movwf    temp2        ;   bcd fashion
    movfw    bcd1
    movwf    temp1
    movfw    bcd0
    movwf    temp0
    bcf    cy0
    bcf    cy1

bcdadd    movlw    66H        ;BCD double the accumulator
    addwf    bcd0,f       
    addwf    bcd1,f       
    addwf    bcd2,f       

    movfw    temp0
    addwf    bcd0,f
    skpnc
    bsf    cy0
    movlw    0A0H
    movwf    temp0
    clrw
    skpdc
    movlw    0FAH
    skpc
    addwf    temp0,W
    addwf    bcd0,f

    btfsc    cy0
    incf    temp1,f
    movfw    temp1
    addwf    bcd1,f
    skpnc
    bsf    cy1
    movlw    0A0H
    movwf    temp1
    clrw
    skpdc
    movlw    0FAH
    skpc
    addwf    temp1,w
    addwf    bcd1,f

    btfsc    cy1
    incf    temp2,f
    movfw    temp2
    addwf    bcd2,f
    movlw    0A0H
    movwf    temp2
    clrw
    skpdc
    movlw    0FAH
    skpc
    addwf    temp2,w
    addwf    bcd2,f

    rlf    bin0,f        ;rotate a bit out of
    rlf    bin1,f        ;the exponential accumulator
    rlf    bin2,f
    skpnc
    incf    bcd0,f        ;add it to the BCD accumulator
    decfsz    bitcnt,f
    goto     bloop        ;continue until all bits have been processed
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
Here's a binary to BCD conversion routine I wrote a little over 100 years ago. You can extract out the BCD addition code where the BCD accumulator is doubled by summing temp2:0 (a copy of bcd2:0) into bcd2:0.

Code:
;*******  Convert Raw 24 Bit Number in bin2:0 to packed BCD in bcd2:0 **********

;must define two carry flags cy0 and cy1
;i.e:
;#define cy0    flags,0
;#define cy1    flags,1
     
binbcd    clrf    bcd2        ;ready for BCD conversion
    clrf    bcd1        ;clear BCD accumulators
    clrf    bcd0
    movlw    24        ;24 bits worth of processing
    movwf    bitcnt        ;one bit at a time

bloop    movfw    bcd2        ;prepare to double BCD number in
    movwf    temp2        ;   bcd fashion
    movfw    bcd1
    movwf    temp1
    movfw    bcd0
    movwf    temp0
    bcf    cy0
    bcf    cy1

bcdadd    movlw    66H        ;BCD double the accumulator
    addwf    bcd0,f      
    addwf    bcd1,f      
    addwf    bcd2,f      

    movfw    temp0
    addwf    bcd0,f
    skpnc
    bsf    cy0
    movlw    0A0H
    movwf    temp0
    clrw
    skpdc
    movlw    0FAH
    skpc
    addwf    temp0,W
    addwf    bcd0,f

    btfsc    cy0
    incf    temp1,f
    movfw    temp1
    addwf    bcd1,f
    skpnc
    bsf    cy1
    movlw    0A0H
    movwf    temp1
    clrw
    skpdc
    movlw    0FAH
    skpc
    addwf    temp1,w
    addwf    bcd1,f

    btfsc    cy1
    incf    temp2,f
    movfw    temp2
    addwf    bcd2,f
    movlw    0A0H
    movwf    temp2
    clrw
    skpdc
    movlw    0FAH
    skpc
    addwf    temp2,w
    addwf    bcd2,f

    rlf    bin0,f        ;rotate a bit out of
    rlf    bin1,f        ;the exponential accumulator
    rlf    bin2,f
    skpnc
    incf    bcd0,f        ;add it to the BCD accumulator
    decfsz    bitcnt,f
    goto     bloop        ;continue until all bits have been processed
Many thanks, Joey. This will come in quite handy. You've just saved me valuable time.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
So I went through the previously posted examples. And this is my thoroughly commented version of the code. I don't know if it can be made more efficient without using additional memory resources. But feel free to criticize it if you think it can be improved... it's amazing how much code could've been saved if the DAW instruction had been available.

Code:
;*****************************************************************************************
;                     perform the BCD addition of CNT01 = CNT01 + CNT00
;          STATUS, C flag will be set if the result is greater than or equal to 100
;
; Working logic:
;    - perform an HEX addition of both numbers and store the result in CNT01
;    - if the low digit overflowed (DC=1) or is greater than 9, then add 6 to it and let
;     said addition propagate to the high digit
;    - if the addition overflowed (C=1), or the high digit is greater than 9, then add 6
;     to the high digit and set the STATUS, C flag
;*****************************************************************************************
Add_CNT00_to_CNT01_bcd:

      ; CNT00, CNT01 and ADDSTAT are a previously declared registers and reside in
      ;common RAM.
      ; MovLf is a macro that loads a literal into a register
      ; MovF2F is a macro that copies the contents of one register into another

       MovLf 0x37, CNT00
       MovLf 0x94, CNT01
  
       movf CNT00, w
       addwf CNT01, f                 ;CNT01 = CNT00 + CNT01
       MovF2F STATUS, ADDSTAT         ;save the STATUS resulting from the previous
                                      ;additon  

       ;add 6 to the result's low digit if the low digit overflowed or is greater than 9
       btfsc ADDSTAT, DC              ;add 6 to the low digit if said digit overflowed
        call Add6_CNT01_Ld            ;with the previous addition
       btfsc ADDSTAT, DC
        goto Process_High_Digit       ;skip if 6 has already been added to the low digit
       call Test_LD_GT9               ;otherwise test if the low digit is greater than 9
                                      ;and add 6 to it if that is the case

       ;add 6 to the result's high digit if the high digit overflowed or is greater than 9
Process_High_Digit:
       btfsc ADDSTAT, C
        call Add6_CNT01_Hd            ;add 6 to the high digit if the highs digit overflowed
       btfsc ADDSTAT, C             
        goto Exit_Add_CNT00_to_CNT01_bcd ;skip if 6 has already been added to the high digit
       call Test_HD_GT9               ;otherwise test if the high digit is greater than 9
                                      ;and add 6 to it if that was the case

                                      ;if either of the previous two calls were made then the
                                      ;STATUS, C bit has been be set so that it can be used
Exit_Add_CNT00_to_CNT01_bcd:          ;in further logic
    return


;-----------------------------------------------------------------------------------------
;          Test if CNT01's low digit is greater than 9, and add 6 to it if it is
;-----------------------------------------------------------------------------------------
Test_LD_GT9:
     movf CNT01, w
     sublw 0x09                   ;C=0 if W>9  ...  C=1 if W<=9
     btfss STATUS, C
      call Add6_CNT01_Ld
    return


;-----------------------------------------------------------------------------------------
;                               Add 6 to CNT01's low digit (nibble)
;-----------------------------------------------------------------------------------------
Add6_CNT01_Ld:
       movf CNT01, w
       addlw 0x06                 ;add 6 to CNT01's low nibble
       movwf CNT01
    return
  
  
;-----------------------------------------------------------------------------------------
;  Test if CNT01's low digit is greater than 9, and add 6 to it (also setting STATUS, C
; to one) if it is.
;-----------------------------------------------------------------------------------------
Test_HD_GT9:
     swap CNT01, w                ;place CNT01's high digit at the low digit
     andlw d'00001111'            ;filter the previously swaped high digit
     sublw 0x09                   ;C=0 if W>9  ...  C=1 if W<=9
     btfss STATUS, C
      call Add6_CNT01_Hd
    return


;-----------------------------------------------------------------------------------------
;              Add 6 to CNT01's high digit (nibble) and set STATUS, C to one
;-----------------------------------------------------------------------------------------
Add6_CNT01_Hd:
       movlw 0x60
       addwf CNT01, f             ;add 6 to CNT01's high digit
       setbf STATUS, C
    return
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
This may be a bit better. It's adapted from some old code. I tested the various combination of nibble values and inter-nibble propagation and it seems to work. YMMV
Run it in the simulator. Set a breakpoint where noted then run it. Result is CNT00 and Carry flag to support more than 2 digits.
There are some optimizations you can do by ORing the STATUS results of the original sum and digit value tests in STATUStemp but this way was easier to debug. It probably doesn't need to swap STATUS to preserve all flags either.
MPASM 5.10 but should be easy to move to pic-as.

Let us know what you wind up with.
Good luck!

EDIT: cleaned up comment noted by @cmartinez in #533

BCD Adder:
;******************* BCDMath.asm  ****************************
    ; Packed BCD addition
    ; 2 digits shown, Carry out can be propagated for more digits
    ; Emulates DA A instruction from 8051 and DAW from 18F

#include "p16lf1823.inc"

; CONFIG1
; __config 0xFFE4
__CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _CPD_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_ON & _FCMEN_ON
; CONFIG2
; __config 0xFFFF
__CONFIG _CONFIG2, _WRT_OFF & _PLLEN_ON & _STVREN_ON & _BORV_LO & _LVP_ON


    cblock  70h
CNT00:    2        ; addend
CNT01:  2        ; augend
STATUStemp: 1        ; swapped image of status to accumulate DC, C flags
    endc

swappedDC   equ    5    ; where DC and C flags are in swapped STATUS image
swappedC    equ    4

    ORG 0
start:
    nop            ; breakpoint to load values
    nop

    ; Do one byte of packed BCD
    ; CNT00 = CNT00 + CNT01

    movf    CNT01,W    ; add packed BCD
    addwf   CNT00,F    ; save intermediate result

            ; adjust sum to packed BCD and Carry out
 
            ; Save C and DC from original addition
    swapf   STATUS,W    ; get status without changing flags
    movwf   STATUStemp    ; save swapped image with DC, C flag from addition
            ; don't really need to use swap as moves don't affect C, DC

    movlw   66h        ; Add 66h to see iff any nibble is > 9
    addwf   CNT00,W    ; just interested in C, DC flags

    movlw   0        ; now, compute adjustment on original sum in W 
 
    btfsc   STATUStemp,swappedDC
    iorlw   06h        ; add 06h iff DC from original sum
    btfsc   STATUS,DC
    iorlw   06h        ; OR iff DC set from value test

    btfsc   STATUStemp,swappedC
    iorlw   60h        ; add 60h iff carry from original sum
    btfsc   STATUS,C
    iorlw   60h        ; OR carry from value test

    addwf   CNT00,F    ; add computed adjustment factor to orginal sum

            ; recover carry from original sum
    btfsc   STATUStemp,swappedC
    setc
            ; C:CNT00 is packed BCD sum plus carry
            ; can propagate carry into subsequent bytes

    goto    start

    END
 
Last edited:

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
This may be a bit better. It's adapted from some old code. I tested the various combination of nibble values and inter-nibble propagation and it seems to work. YMMV

This is embarrassing, but it seems that I don't really understand the behavior of the DC bit in the STATUS register.

In the datasheet, said bit is described as:

1683424450844.png

And the way I've been understanding it is that said bit is set if the low nibble overflows past F, incrementing the high nibble when that happens.

But the way I read your code, said nibble is set if it overflows past 9 ... is that the case?
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
I think I understand now ... this is the key to your code:

Code:
       movlw 66h         ;Add 66h to see iff any nibble is > 9
       addwf CNT00, w    ;just interested in C, DC flags
 

JohnInTX

Joined Jun 26, 2012
4,787
And the way I've been understanding it is that said bit is set if the low nibble overflows past F, incrementing the high nibble when that happens.

But the way I read your code, said nibble is set if it overflows past 9 ... is that the case?
No, you are right. Bad comment. If the DC flag did work like that, we would not have to do the additional test on the nibble values. Good catch.

There are some optimizations you can do by ORing the STATUS results of the original sum and digit value tests in STATUStemp but this way was easier to debug.
ORing the two STATUS results worked as expected and saved a few bytes.

Have a good evening!
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
Many thanks for your help, John. I finally understand your code now. It's the hell of a lot simpler than my previous line of thought.

Here's my version of your code, which includes the addition of C to the original sum. Also I added line 45 to correct a possible mistake in the working logic.

Code:
;*****************************************************************************************
;                  Perform the BCD addition of CNT01 = CNT01 + CNT00 + C
;
;          STATUS, C flag will be set if the result is greater than or equal to 100
;
; Working logic:
;    - perform an HEX addition of both numbers and store the result in CNT01
;    - if the low digit overflowed (DC=1) or is greater than 9, then add 6 to it and let
;     the overflow of said addition propagate to the high digit
;    - if the addition overflowed (C=1), or the high digit is greater than 9, then add 6 
;     to the high digit and set the STATUS, C flag
;*****************************************************************************************
Add_CNT01_to_CNT00_bcd:

     
      ; MovF2F is a macro that copies the contents of one register into another
      ; CNT00, CNT01 and SavedSTATUS reside in common RAM. 

       movf CNT01, w     ;perform CNT00 = CNT00 + CNT01 + C
       addwfc CNT00, f   ;the previous instruction affects Z, but not C in STATUS

       ;now adjust sum to packed BCD and Carry out
       MovF2F STATUS, SavedSTATUS ;Save C and DC from the previous addition
                         ;this previous instruction affects the Z flag, but not DC and C

       movlw 0x66        ;Add 0x66 to see if any nibble is > 9
       addwf CNT00, w    ;STATUS now contains the flags generated by this second addition
                         ; if DC=1 then the low nibble > 9
                         ; if C=1 then the high nibble > 9

       clrw              ;now, compute adjustment on original sum in W
                         ;this previous instruction affects the Z flag, but not DC and C   
       btfsc SavedSTATUS, DC
       iorlw 0x06        ;add 0x06 if DC from original sum if set
       btfsc STATUS, DC
       iorlw 0x06        ;OR if DC is set from the value test (nibble > 9)

       btfsc SavedSTATUS, C
       iorlw 0x60        ;add 0x60 if C from original sum is set
       btfsc STATUS, C
       iorlw 0x60        ;OR if C from the value test indicates that nibble > 9

       addwf CNT00, f    ;add computed adjustment factor to orginal sum

       bcf STATUS, C     ;recover carry from original sum
       btfsc SavedSTATUS, C
       bsf STATUS, C
    
      ;CNT00 is now a packed BCD sum. C is set or cleared so it can propagate into 
      ;subsequent operations 

    return
 

joeyd999

Joined Jun 6, 2011
6,304
Combining all our ideas, I wrote this today to sum to BCD numbers of arbitrary length.

Set FSR0 -> first operand (and result)
Set FSR1 -> second operand
Set WREG = number of bytes

call AddWBCD.

The last carry is returned for overflow information.

Should work, but not tested.


Code:
;@JoeyD -- 20230507

#define SavedC  SavedStatus,C+4
#define SavedDC SavedStatus,DC+4


*****************************************************
;** AddWBCD                                        **
;** Add (FSR0) = (FSR0) + (FSR1)                   **
;** wreg = number of BCD bytes (digits / 2)        **
;** return with final carry                        **
;** second operand is preserved                    **
;** requires registers "SavedStatus" and "bytecnt" **
;****************************************************

AddWBCD

    movwf   bytecnt              ;save number of bytes
    clrf    SavedStatus          ;preclear first carry
  
bcdloop

    movlw   0x66                 ;prepare for BCD addition
    addwf   indf0,f              ;fsr0 -> first addend and result

    movfw   indf1                ;get 2nd addend
    btfsc   SavedC               ;incoming carry?
    incf    indf1,w              ;yes, addend+1
    addwf   indf0,f              ;add it in

    swapf   status,w             ;save resulting C and DC
    movwf   SavedStatus

    clrw                         ; C and DC, adjustment   = 0x00
    btfss   SavedDC              ; C and NDC, adjustment  = 0xFA
    addlw   0xFA
    btfss   SavedC               ;NC and DC,  adjustment  = 0xA0
    addlw   0xA0
    addwf   indf0,f              ;NC and NDC, adjustment = 0xFA + 0xA0 = 0x9A

    incf    fsr0h,f                 ;update pointers
    incfsz  fsr0l,f
    decf    fsr0h,f

    incf    fsr1h,f           
    incfsz  fsr1l,f
    decf    fsr1h,f

    decfsz  bytecnt,f           ;do for all bytes
    goto    bcdloop

    swapf   SavedStatus,w
    movwf   status                 ;return with last carry

    return
 
Last edited:

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
Combining all our ideas, I wrote this today to sum to BCD numbers of arbitrary length.

Set FSR0 -> first operand (and result)
Set FSR1 -> second operand
Set WREG = number of bytes

call AddWBCD.

The last carry is returned for overflow information.

Should work, but not tested.


Code:
;@JoeyD -- 20230507

#define SavedC  SavedStatus,C+4
#define SavedDC SavedStatus,DC+4


*****************************************************
;** AddWBCD                                        **
;** Add (FSR0) = (FSR0) + (FSR1)                   **
;** wreg = number of BCD bytes (digits / 2)        **
;** return with final carry                        **
;** second operand is preserved                    **
;** requires registers "SavedStatus" and "bytecnt" **
;****************************************************

AddWBCD

    movwf   bytecnt              ;save number of bytes
    clrf    SavedStatus          ;preclear first carry
 
bcdloop

    movlw   0x66                 ;prepare for BCD addition
    addwf   indf0,f              ;fsr0 -> first addend and result

    movfw   indf1                ;get 2nd addend
    btfsc   SavedC               ;incoming carry?
    incf    indf1,w              ;yes, addend+1
    addwf   indf0,f              ;add it in

    swapf   status,w             ;save resulting C and DC
    movwf   SavedStatus

    clrw                         ; C and DC, adjustment   = 0x00
    btfss   SavedDC              ; C and NDC, adjustment  = 0xFA
    addlw   0xFA
    btfss   SavedC               ;NC and DC,  adjustment  = 0xA0
    addlw   0xA0
    addwf   indf0,f              ;NC and NDC, adjustment = 0xFA + 0xA0 = 0x9A

    incf    fsr0h,f                 ;update pointers
    incfsz  fsr0l,f
    decf    fsr0h,f

    incf    fsr1h,f          
    incfsz  fsr1l,f
    decf    fsr1h,f

    decfsz  bytecnt,f           ;do for all bytes
    goto    bcdloop

    swapf   SavedStatus,w
    movwf   status                 ;return with last carry

    return
Nice! ... very berry nice ... you just couldn't resist a challenge could ya? :)
 

joeyd999

Joined Jun 6, 2011
6,304
Nice! ... very berry nice ... you just couldn't resist a challenge could ya? :)
Well, I was getting disturbed watching you guys double-muck around with the status flags. I had to do something!

Edit: besides, my life has been much regrettably consumed by C the last many months. Writing some PIC16 code is like coming up for air after 5 minutes underwater, its primitiveness compared to PIC18 notwithstanding.
 
Last edited:

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
Well, I was getting disturbed watching you guys double-muck around with the status flags. I had to do something!

Edit: besides, my life has been much regrettably consumed by C the last many months. Writing some PIC16 code is like coming up for air after 5 minutes underwater, its primitiveness compared to PIC18 notwithstanding.
Well I'm grateful that my little conundrum had the benefit of (at least for a moment) clearing your c-induced brain fog... :p
 
Top