XC8 v2.31 Help: Integer Arithmetic With Numbers Larger Than 32 Bits? (Solved)

Status
Not open for further replies.

Mike - K8LH

Well-Known Member
Hi guys and gals:

How do I implement the following formula, which has an intermediate result that is wider than 32 bits, using integer math in C? I know how to do it in assembler but I've been forced to use the 64-bit MPLABX v5.40 and XC8 v2.31 in order to use the new 18F16Q40 chip and XC8 no longer supports in-line assembly code...

TIA... Cheerful regards, Mike
Code:
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Fosc 32000000-Hz (8000000-Hz 4xPLL) - convert decimal 8-digit   ~
; input (range 100000..15999900) into a 20-bit 'NCO1INC' value.   ~
;                                                                 ~
; nco1inc = bin = INT((frequency/100)*65536/10000+0.5)            ~
;                                                                 ~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
Will adding an L to the end of each number cause it to calculate in64 bits? Not at home ATM so can't check.

Mike.
 
I've tried using "unsigned long long" types for the variables but here's what I get;

Code:
ROMEMU1802v00b.c:468:20: warning: (1516) compiler does not support 64-bit integers on the target architecture
ROMEMU1802v00b.c:470:20: warning: (1516) compiler does not support 64-bit integers on the target architecture

Perhaps there's a way to re-write the formula so that intermediate values fit within "uint32_t" types?

As an example; For an NCO output frequency of 1843200-Hz you should get a rounded NCO1INC value of 120796.

Also, I can get the desired results using "float" but it really bloats the program...
 
Mike, I just added the following to a project and it compiles fine,
Code:
    asm("MOVLW20");
    asm("MOVWF0x20");

Mike.
 
Mike, I just added the following to a project and it compiles fine,
Code:
    asm("MOVLW20");
    asm("MOVWF0x20");

Mike.
I can get XC8 v2.31 to do that too. The problem is trying to access any C variables. I've tried 'static' and non-static and all the operand variations and I either get "unrecognized symbol" or "error: (1356) fixup overflow referencing psect bssCOMRAM (0x517) into 1 byte at 0x289E/0x1 -> 0x289E (dist/default/production\ROMEMU1802.X.production.o 148/0x2)"

XC8 versions prior to v2.31 accept in-line assembly, like the 16F example below, without complaint;

Code:
/************************************************************************
 *                                                                      *
 *                                  *************************************/
   void led_char(char ascii)        // send ASCII 5x7 character font
   { static char ndx;               //
     asm("addlw  -32            "); // ascii 32..127 minus offset     |02
     asm("movwf  led_char@ndx   "); // save table index, 0..95        |02
     asm("lsrf   WREG,F         "); // int(index * 2.5) -> 0..237     |02
     asm("addwf  led_char@ndx,W "); //  "                             |02
     asm("addwf  led_char@ndx,W "); //  "                             |02
     asm("movlb  NVMADRL/128    "); // bank 16                        |16
     asm("movwf  NVMADRL        "); //                                |16
     asm("movlw  Font5x7/256    "); //                                |16
     asm("movwf  NVMADRH        "); //                                |16
/*                                                                      *
 *   Extract five bytes of font data from three words of memory. An     *
 *   even character uses word 0 hi + lo, 1 hi + lo, and 2 hi while      *
 *   odd characters use word 0 lo, 1 hi + lo, and 2 hi + lo.            *
 *                                                                      */
     asm("call   _rdflash       "); // read 1st word (2 bytes)        |03
     asm("btfss  led_char@ndx,0 "); // odd char? yes, skip, else      |03
     asm("call   _led_write     "); // send hi byte (even character)  |02
     asm("movlb  NVMDATL/128    "); // bank 16                        |16
     asm("movf   NVMDATL,W      "); //                                |16
     asm("call   _led_write     "); // send lo byte                   |02
     asm("call   _rdflash       "); // read 2nd word (2 bytes)        |16
     asm("call   _led_write     "); // send hi byte                   |02
     asm("movlb  NVMDATL/128    "); // bank 16                        |16
     asm("movf   NVMDATL,W      "); //                                |16
     asm("call   _led_write     "); // send lo byte                   |02
     asm("call   _rdflash       "); // read 3rd word (2 bytes)        |16
     asm("call   _led_write     "); // send hi byte                   |02
     asm("movlb  NVMDATL/128    "); // bank 16                        |16
     asm("movf   NVMDATL,W      "); //                                |16
     asm("btfsc  led_char@ndx,0 "); // even char? yes, skip, else     |16
     asm("call   _led_write     "); // send lo byte (odd characters)  |02
   }                                //
 
To use 'unsigned long long' type you need to set XC8 to C99 mode.

But, if you want to use 32-bit math...

65536/10000 = 6.5536

For numbers in the range of 0-159999 (1599900/100), you can multiply by 13422 and then divide by 2048 (shift right by 11)
That gives an error of 0.0016927719%

13422/2048 = 6.5537109
 
To use 'unsigned long long' type you need to set XC8 to C99 mode.
Thank you! I switched to C99 and 64-bit arithmetic is working.

I got rid of the floating point code and shaved 2700 bytes off my program. The 64-bit math only seems to cost about 500-600 bytes. Not nearly as efficient as my assembly language 'ncoCalc' routine (57 instructions) but I'm happy.

Thanks for the help, guys. Stay safe. Cheerful regards, Mike
 
nco1inc = bin = INT((frequency/100)*65536/10000+0.5)

change the equation to two steps.
Ncolinc = bin = ceil(frequency * 0.065536)

Your /10000 and /100 is the same as dividing by 1,000,000 and you may as well just apply that to your 65536 since it is a constant.
 
Thank you for the suggestion. That's similar to one of the many formulas I tested when I was using floating point math.

The formula with the /100 and /10000 operations was used in custom assembly language 'ncoCalc' routines (Fosc 32-MHz) for 16F18313 and 16F18325 projects (excerpts below).

Code:
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  unpacked 8-digit decimal (each digit 0..9) to a 24-bit binary  ~
;  number in acc[2..0].  Range 0..16,777,215 however NCO range    ~
;  is 100,000..15,999,900 in 100-Hz steps (acc = frequency/100).  ~
;                                                                 ~
;  nco1inc = bin = INT((frequency/100)*65536/10000+0.5)           ~
;                                                                 ~
;  isochronous, 534/678 cycles   Mike McLaren, K8LH, 28-Jan-'20   ~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        radix   dec
        cblock  0x70
bcd:8                           ; 8-digit decimal/bcd number
acc:3                           ; 24-bit binary accumulator
ctr                             ; loop ctr var'
        endc
rem     equ     bcd+4           ; 1-byte 'remainder'
bin     equ     bcd+6           ; 5-byte 'acc' * 65536

ncoCalc
        movlw   24              ;                                 |
        movwf   ctr             ;                                 |
        movlw   10              ;                                 |
bcd2lp
        lsrf    bcd+7,F         ;                                 |
        skpnc                   ;                                 |
        addwf   bcd+6,F         ; add 10                          |
        lsrf    bcd+6,F         ;                                 |
        skpnc                   ;                                 |
        addwf   bcd+5,F         ; add 10                          |
        lsrf    bcd+5,F         ;                                 |
        skpnc                   ;                                 |
        addwf   bcd+4,F         ; add 10                          |
        lsrf    bcd+4,F         ;                                 |
        skpnc                   ;                                 |
        addwf   bcd+3,F         ; add 10                          |
        lsrf    bcd+3,F         ;                                 |
        skpnc                   ;                                 |
        addwf   bcd+2,F         ; add 10                          |
        lsrf    bcd+2,F         ;                                 |
;       skpnc                   ; skipping 2 least significant    |
;       addwf   bcd+1,F         ; digits effectively divides      |
;       lsrf    bcd+1,F         ; the bcd[7..0] value by 100      |
;       skpnc                   ;                                 |
;       addwf   bcd+0,F         ;                                 |
;       lsrf    bcd+0,F         ;                                 |
        rrf     acc+2,F         ;                                 |
        rrf     acc+1,F         ;                                 |
        rrf     acc+0,F         ;                                 |
        decfsz  ctr,F           ; all 24 bits? yes, skip, else    |
        bra     bcd2lp          ;                                 |

        call    div100          ; divide 'bin' by 10000           |
        call    div100          ;  "                              |

        movlw   -50             ; rounding                        |
        addwf   rem,W           ;  "                              |
        movlw   0               ;  "                              |
        addwfc  bin+0,F         ;  "                              |
        addwfc  bin+1,F         ;  "                              |
        addwfc  bin+2,W         ;  "                              |
        banksel NCO1INCU        ; bank 09 on 16F18313/325         |09
        movwf   NCO1INCU        ; set NCO1INCU                    |09
        movf    bin+1,W         ;                                 |09
        movwf   NCO1INCH        ; set NCO1INCH                    |09
        movf    bin+0,W         ;                                 |09
        movwf   NCO1INCL        ; set NCO1INCL                    |09
        return                  ;                                 |09

div100
        movlw   40              ;                                 |
        movwf   ctr             ;                                 |
        clrf    rem             ; clear 'remainder'               |
divLp   rlf     bin+0,W         ;                                 |
        rlf     bin+1,F         ;                                 |
        rlf     bin+2,F         ;                                 |
        rlf     bin+3,F         ;                                 |
        rlf     bin+4,F         ;                                 |
        rlf     rem,F           ;                                 |
        movlw   100             ;                                 |
        subwf   rem,W           ; Does 100 go in?                 |
        skpnc                   ; borrow? no, skip, else          |
        movwf   rem             ; update remainder (C = 1)        |
        rlf     bin+0,F         ; shift in the borrow bit         |
        decfsz  ctr,F           ; all 40 bits? yes, skip, else    |
        bra     divLp           ; loop                            |
        return                  ;                                 |
 
Last edited:
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…