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.

32bit compare - PIC assembly - better way?

Status
Not open for further replies.

augustinetez

Active Member
At the moment, some of my projects relating to the AD9850 are using two different routines to test if the frequency is above or below preset levels.

Obviously a waste of program space that I would like to rectify (I didn't write the currently used routines, they came from the various PIC-El files).

So the question is, can the following code (found on PIClist), which I am using in another program, be easily expanded to do 32bit compares or is there an easier way?
I have spent a fair amount of time searching the 'net, but most of what I've found makes my head hurt.

Code:
;*******************************************************************
; A not too optimised 16 bit compare routine for 16bit absolute
; values,    Antonio L Benci (PIClist)
; ie 0 -> 65536.
; Compare WORD to COMP (a word value).
; If WORD = COMP return with 00
; If WORD > COMP return with 01
; If WORD < COMP return with 80
;*******************************************************************

hword   equ     0x10        ; storage for high byte of WORD
lword   equ     hword+1     ; storage for low byte of WORD
hcomp   equ     0x12        ; storage for high byte of COMP
lcomp   equ     hcomp+1     ; storage for low byte of COMP

COMP
   movfw   hcomp           ; get high byte of comp value
   subwf   hword           ; subtract values
   btfsc   status,z        ; first check if result is 0
   goto    COMPL           ; if zero compare low bytes
   btfsc   status,c        ; else test carry bit
   retlw   0x01            ; if WORD > COMP, return with 01h
   retlw   0x80            ; if WORD < COMP, return with 80h

COMPL
   movfw   lcomp           ; get low byte of comp value
   subwf   lbyte           ; subtract values
   btfsc   status,z        ; first check if result is 0
   retlw   0x00            ; if result is 0, return with 00
   btfsc   status,c        ; if c set then WORD > COMP
   retlw   0x01            ; if WORD > COMP, return with 01h
   retlw   0x80            ; if WORD < COMP, return with 80h

end
;******************************************************************

The currently used code routines are:

Code:
; *******************************************************************************
; *                                        *
; * Purpose:    Check if freq exceeds the upper limit.                *
; *                                        *
; *   Input:    The 32 bit values in freq                    *
; *                                        *
; *  Output:    If freq is below the limit, it is unchanged.  Otherwise, it is    *
; *        set to equal the upper limit.                    *
; *                                        *
; *******************************************************************************
;
check_add
;
;    Check the most significant byte.
;
    movlw    0xFF-limit_3    ; Get (FF - limit of high byte)
    addwf    freq_3,w    ; Add it to the current high byte
    btfsc    STATUS,C    ; Was high byte too large?
    goto    set_max        ; Yes, apply limit
    movlw    limit_3        ; Get high limit value
    subwf    freq_3,w    ; Subtract the limit value
    btfss    STATUS,C    ; Are we at the limit for the byte?
    goto    exit1        ; No, below.  Checks are done.
;
;    Check the second most significant byte.
;
    movlw    0xFF-limit_2    ; Get (FF - limit of next byte)
    addwf    freq_2,w    ; Add it to the current byte
    btfsc    STATUS,C    ; Is the current value too high?
    goto    set_max        ; Yes, apply the limit
    movlw    limit_2        ; Second limit byte
    subwf    freq_2,w    ; Subtract limit value
    btfss    STATUS,C    ; Are we at the limit for the byte?
    goto    exit1        ; No, below.  Checks are done.
;
;    Check the third most significant byte.
;
    movlw    0xFF-limit_1    ; Get (FF - limit of next byte)
    addwf    freq_1,w    ; Add it to the current byte
    btfsc    STATUS,C    ; Is the current value too high?
    goto    set_max        ; Yes, apply the limit
    movlw    limit_1        ; Third limit byte
    subwf    freq_1,w    ; Subtract limit value
    btfss    STATUS,C    ; Are we at the limit for the byte?
    goto    exit1        ; No, below.  Checks are done.
;
;    Check the least significant byte.
;
    movlw    limit_0        ; Fourth limit byte
    subwf    freq_0,w    ; Subtract limit value
    btfss    STATUS,C    ; Are we at the limit for the byte?
    goto    exit1        ; No, below.  Checks are done.
set_max
    movlw    limit_0        ; Get least significant limit
    movwf    freq_0        ; Set it in freq
    movlw    limit_1        ; Get the next byte limit
    movwf    freq_1        ; Set it in freq_1
    movlw    limit_2        ; Get the next byte limit
    movwf    freq_2        ; Set it in freq_2
    movlw    limit_3        ; Get the most significant limit
    movwf    freq_3        ; Set it in freq_3
exit1
    return            ; Return to the caller

And:

Code:
; *******************************************************************************
; *                                        *
; * Function:    low_limit_chk                            *
; *                                        *
; * Purpose:    Test the new frequency to see if it is above the lower band    *
; *        limit. If not, the frequency is set to the band lower limit.    *
; *                                        *
; * Input:    Freq_0...3 and Low_limit_0...3                    *
; *                                        *
; * Output:    Original frequency if above low limit or low_limit_0..3 in    *
; *        Freq_0...3                            *
; *                                        *
; *******************************************************************************
;
low_limit_chk
;       Check the most significant byte.
;
    btfsc    freq_3,7
    goto    set_low           ; Yes, set to lower frequency limit
    movf    freq_3,w
    subwf    low_limit_3,w
    btfss     STATUS,C          ; Are we at the limit for the byte?
    goto    limit_exit        ; No, above.
    btfss    STATUS,Z          ; Are the bytes equal?
    goto    set_low           ; No, so vfo_X_3 > limit_3.
;
;       Check the second most significant byte when MSB equals limit_3
;
    movf    freq_2,w
    subwf    low_limit_2,w
    btfss    STATUS,C          ; Are we at the limit for the byte?
    goto    limit_exit        ; No, above.  Check next.
    btfss    STATUS,Z          ; Might they be equal?
    goto    set_low           ; Nope, so vfo_X_2 > limit_2
;
;       Check the third most significant byte.
;
    movf    freq_1,w
    subwf    low_limit_1,w
    btfss    STATUS,C          ; Are we at the limit for the byte?
    goto    limit_exit        ; No, above.  Checks are done.
    btfss    STATUS,Z          ; Check if the bytes are equal
    goto    set_low           ; No, so vfo_X_1 > limit_1
;
;       Check the least significant byte.
;
    movf    freq_0,w
    subwf    low_limit_0,w
    btfss    STATUS,C          ; Are we at the limit for the byte?
    goto    limit_exit        ; No, above.  Checks are done.     
;
;    The frequency is below the band lower limit. Set frequency to the
;    band starting frequency.       
set_low
;
    movf    low_limit_0,w
    movwf    freq_0
    movf    low_limit_1,w
    movwf    freq_1
    movf    low_limit_2,w
    movwf    freq_2
    movf    low_limit_3,w
    movwf    freq_3
;
limit_exit
    call    invert_fstep    ; Put fstep back to original value
    return            ; Return to caller
 
This one looks to be fast and well optimised:



Copied with credits, in case that archive link vanishes:
Code:
Below is code that does a 32 bit compare.  It uses the flags byte and other
definitions in STD.INS.ASPIC, which can be found at
http://www.embedinc.com/pic.  It should be pretty trivial to rip out 16 bits
of the compare.


;   Subroutine COMPA
;
;   Compare REGA and REGB.  The arithmetic flags will be set for REGA
;   compared to REGB.  For example, the GT flag will be set if REGA = 5
;   and REGB = 3.
;
.compa   code
        glbsub  compa, noregs

        movlw   ~flag_ar    ;get mask for all but arithmetic flags
        andwf   flags       ;init all the arithmetic flags to unset

        movf    regb+3, w
        subwf   rega+3, w   ;compare byte 3 (high byte)
        skip_z
        goto    compa_ne

        movf    regb+2, w
        subwf   rega+2, w   ;compare byte 2
        skip_z
        goto    compa_ne

        movf    regb+1, w
        subwf   rega+1, w   ;compare byte 1
        skip_z
        goto    compa_ne

        movf    regb+0, w
        subwf   rega+0, w   ;compare byte 0
        skip_z
        goto    compa_ne
;
;   Equal
;
        bsf     flags, flagb_eq
        return
;
;   The two values are definitely not equal, and the C flag is set according
;   to the comparison.
;
compa_ne
        skip_wgt            ;A is smaller ?
        goto    compa_gt    ;accumulator is larger
        bsf     flags, flagb_lt ;accumulator is smaller
        return
compa_gt                     ;accumulator is larger
        bsf     flags, flagb_gt
        return



*****************************************************************
Olin Lathrop, embedded systems consultant in Devens Massachusetts
(978) 772-3129, olinspamembedinc.com, http://www.embedinc.com
 
Think about the logic involved. Start with the high byte, if it's greater then so is the (32 bit) number, lower then so is the (32 bit) number, if neither then move to the next byte. Repeat until no more bytes and if still going, must be equal.

Mike.
 
That's one of the one's that made my head hurt trying to work out exactly what from the other referenced file is needed.
rega and regb in that are your freq and limit registers.

To check both limits, use it twice with freq and low limit then high limit and freq

If it's out of bounds, copy the low or high limit back to freq.
 
That's one of the one's that made my head hurt trying to work out exactly what from the other referenced file is needed.
Here's a modified version of post #2 with references simplified and expanded inline so you can see what's going on.
I also added a return value in WREG to match your original comparison, so you can remove the 'flags' register
Code:
; variable starting address
 regstart equ 0

;   Declare the 32 bit registers A and B
 rega     equ     regstart
 regb     equ     regstart + 4 

;   Create FLAGS register.  This register receives the result of compare
;   and other operations.  It is always located immediately following the
;   general registers.
 flags    equ     regstart + 8   ;declare FLAGS register address
;
;   Identify particular bits in the FLAGS register.  These constants represent
;   bit numbers so that they can be used directly with bit manipulation
;   instructions.
 flagb_lt equ     0           ;comparison result was "less than"
 flagb_eq equ     1           ;comparison result was "equal"
 flagb_gt equ     2           ;comparison result was "greater than"

;Below is code that does a 32 bit compare.  It uses the flags byte and other
;definitions in STD.INS.ASPIC, which can be found at http://www.embedinc.com/pic
;It should be pretty trivial to rip out 16 bits of the compare.


;   Subroutine COMPA
;
;   Compare REGA and REGB.  The arithmetic flags will be set for REGA
;   compared to REGB.  For example, the GT flag will be set if REGA = 5
;   and REGB = 3
; modified to return comparison result in WREG:
;   REGA = REGB   returns WREG = 0x00
;   REGA > REGB   returns WREG = 0x01
;   REGA < REGB   returns WREG = 0x80
;
compa
        clrf    flags       ;init all the arithmetic flags to unset

        movf    regb+3, w
        subwf   rega+3, w   ;compare byte 3 (high byte)
        btfss   status, z   ;skip_z
        goto    compa_ne

        movf    regb+2, w
        subwf   rega+2, w   ;compare byte 2
        btfss   status, z   ;skip_z
        goto    compa_ne

        movf    regb+1, w
        subwf   rega+1, w   ;compare byte 1
        btfss   status, z   ;skip_z
        goto    compa_ne

        movf    regb+0, w
        subwf   rega+0, w   ;compare byte 0
        btfss   status, z   ;skip_z
        goto    compa_ne
;
;   Equal
;
        bsf     flags, flagb_eq
        movlw    0x00     ; RETURN VALUE REGA=REGB
        return
;
;   The two values are definitely not equal, and the C flag is set according
;   to the comparison.
;
compa_ne
        btfsc   status, c   ;skip if a borrow occurred (A is smaller)
        goto    compa_gt    ;accumulator is larger
        bsf     flags, flagb_lt ;accumulator is smaller
        movlw    0x80       ; RETURN VALUE REGA < REGB
        return
compa_gt                     ;accumulator is larger
        bsf     flags, flagb_gt
        movlw    0x01       ; RETURN VALUE REGA > REGB
        return

;*****************************************************************
;Olin Lathrop, embedded systems consultant in Devens Massachusetts
;(978) 772-3129, olinspamembedinc.com, http://www.embedinc.com
 
Thank's all.

Mike, I understand(ish) how it is supposed to work, but the bit tests still mange to trip me up after all this time and I inevitably end up totally confused (which is not hard to do these days).

rjenkinsgb, I worked that out, but the stuff in the other file was not easy to pick out only the bits needed to complete it all, plus I only need to do the comparison once depending on whether the frequency is going up or down once it gets to either end, yes, I just replace the freq with top or bottom limit.

tumbleweed, thankyou, that is much easier to read and yes, I will dump the flags in favour of just using the returned value in w. Hopefully I should be able to try it tomorrow.
 
some of my projects relating to the AD9850 are using two different routines to test if the frequency is above or below preset levels
easily expanded to do 32bit compares or is there an easier way?

I assume that this is the 32 bit tuning word which you are trying to compare.

When I started playing with AD9850s I generated a 32 bit word by counting up/down the output of a quad encoder, and found that starting with the 32bit binary got very messy very quickly.
So I turned the whole thing around and counted the decimal decades.
I made several registers, one for each decade, ie:
10s of Mhz
1s of Mhz
100s of kHz
10s of kHz
etc, down to
1s of Hz

Then to generate the tuning word I just added up the number of bits for each of decades and sent it to the AD9850.

eg for 7031 kHz
I added up
0x20C49BA seven times for the 7MHz
0x53E2D three times for the 30kHz
0x8637 once for the 1kHz
These hex numbers are for a 125MHz clock in an AD9850

Some of this may be a bit long winded and profligate in registers and processor time, but it has worked well for me, and in my confused and addled brain, limit checking on a bunch of registers each with a simple decimal number is easier than checking a 32bit binary number.

JimB
 
Some of this may be a bit long winded and profligate in registers and processor time, but it has worked well for me, and in my confused and addled brain, limit checking on a bunch of registers each with a simple decimal number is easier than checking a 32bit binary number.

Sometimes having a 'eureka moment' produces a nice simple solution, just by doing something slightly differently.
 
I assume that this is the 32 bit tuning word which you are trying to compare.
JimB

Yes, against upper and lower limits.
I remember seeing something a while ago now along the lines you describe, not sure where but it did stand out as being different.

While the above is the main purpose at the moment, I try to keep various code routines as 'universal' as possible for the occasion where they can be used for something entirely different.

This one will have to be pumped up to 40 and 48 bits when I finally get the time to play with some of the bigger DDS chips, there's a couple of '9959's sitting in the cupboard waiting for that day.

'Eureka moments' are getting few and far between around here, I think somebody must be hiding them :)

And speaking of moments - I actually meant flag testing rather than bit testing in my previous post.
 
If you switch to C then it's simply,
Code:
    if(freq>UPPER_LIMIT)
         freq=UPPER_LIMIT;
    if(freq<LOWER_LIMIT)
        freq=LOWER_LIMIT;

Worth the effort.

Mike.
 
I tried learning C many times - I find the syntax more confusing than assembly, especially when a symbol can mean more than one thing depending on how it is used.

Reminds me of the old adage of the camel being designed by a committee.

Got the compare routine working, but now managed to stuff something else up in the process so backtracking to find out where I goofed.
 
So, post lots of line of code, I state, learn C, it'll be four lines. You find it more confusing.

Have a go, I'm happy to help you along. Which part confuses you?

Mike.
 
I bought a book (Beginning C for Microcontrollers) late last year as part of the umpteenth try at learning C and I get some of it, I can do the standard "Hello world" bit and I have written a program from scratch for the Arduino to control an old railways signal box mimic panel, so I get bits and pieces.

Anyway, I got to the section on pointers in the book and must have read the chapter at least 8 times before it dawned on me that, all that is (if related back to PIC assembly) is indirect addressing but one of the confusing bits of it is the use of the * symbol. Apparently, depending on how you use it, it can mean or take on one of at least three different types of operations.

Bottom line, I would love to be able to understand and work with C, I have had the Sourceboost compiler, Microchips bug ridden MPlabX+XC8 and various others which of course all do things differently to each other that just adds to the confusion.

For a language that is meant to be 'standard', there appear to be many non-standard versions around for programming micro's.

Also, I am better at learning by doing as opposed to here's a book - learn it.

So, if you are prepared to guide me through this - I would like to do it by converting one of my existing projects - say the Simple VFO for 12F629/675, but I think we should open another topic rather than do it in this one.
 
So, if you are prepared to guide me through this - I would like to do it by converting one of my existing projects - say the Simple VFO for 12F629/675, but I think we should open another topic rather than do it in this one.
OK, let's give it a try. I suggest using MPLABX as I have that available. I did have, and prefer boostC but somehow lost it over the years. One suggestion is to not use libraries as that is where ALL compilers vary. C is pretty standard but the libraries are someone else's code and can cause confusion. And, pointers are in the advanced section and are not needed for nearly all projects. What was the first project you used the FSR register on?

Mike.
Edit, with only 64 bytes of ram those chips could prove a challenge. :cool:
 
Last edited:
The 12F1840 would be better and I have a 12F1822 which is a slightly smaller version of the 1840 so can actually compile stuff onto it. In fact, the reason I set it up was to write code to generate a variable frequency from ~7Hz to ~7kHz. See this post.

Mike.
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top