This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

How to perform 6 byte addition with efficiency using C code

unsigned long ul1=XX, ul2=YY, ul;
unsigned int ui1=AA, ui2=BB, ui;

ui=ui1+ui2;
ul=ul1+(ul2+(unsigned long)CY);

very inefficient code is generated by C51.
but this can be done using ADC (add with carry) if assembly is used.

Is there any way in C itself, to perform 6 byte addition?

Parents Reply Children

  • I agree; if the size and speed are really important, you'll want an assembler routine to deal with 48-bit integers.

    From the example code, I'm not sure exactly what you're trying to accomplish, other than adding two 48-bit integers.

    You have access to the carry bit in the program status word register. How about something along the lines of (warning, code off the top of my head!):

    sbit carry = PSW^7;
    
    void U48AddEq (U8 data * dst, U8 data * src, U8 len)
        {
        bit c = 0;
    
    	while (len--)
    		{
    	    dst[len] += (U8)carry + src[len];
    		c = carry;
    	    }
    	}
    


    which compiles into the only moderately awful:

                 ; FUNCTION _U48AddEq (BEGIN)
                                               ; SOURCE LINE # 73
    ;---- Variable 'dst' assigned to Register 'R7' ----
    ;---- Variable 'len' assigned to Register 'R3' ----
    ;---- Variable 'src' assigned to Register 'R5' ----
                                               ; SOURCE LINE # 74
                                               ; SOURCE LINE # 75
                     R     CLR     c
                 ?C0002:
                                               ; SOURCE LINE # 77
                           MOV     R6,AR3
                           DEC     R3
                           MOV     A,R6
                     R     xJZ     ?C0004
                                               ; SOURCE LINE # 78
                                               ; SOURCE LINE # 79
                           MOV     A,R5
                           ADD     A,R3
                           MOV     R0,A
                           MOV     A,@R0
                           MOV     R6,A
                           CLR     A
                           RLC     A
                           ADD     A,R6
                           MOV     R6,A
                           MOV     A,R7
                           ADD     A,R3
                           MOV     R0,A
                           MOV     A,R6
                           ADD     A,@R0
                           MOV     @R0,A
                                               ; SOURCE LINE # 80
                     R     MOV     c,C
                                               ; SOURCE LINE # 81
                     R     xJMP    ?C0002
                                               ; SOURCE LINE # 82
    C51 COMPILER V7.02b   MAIN                                                                 03/04/2003 13:39:31 PAGE 4
    
                 ?C0004:
                           RET
    
    




    You could perhaps unroll the loop for specific integer lengths and improve the speed if you want just 6-byte math. You could try the intermediate math in bigger chunks (U16, even U32) and perhaps get a little more efficient.

  • "You have access to the carry bit in the program status word register."

    True, but you have no guarantee that the carry bit at the start of a 'C' statement will necessarily have anything to do with the result of any particular arithmetic operation in the preceding 'C' statement.

    If you need that sort of direct register control, then you must write in assmebler!


  • True; the only reason that the variable "c" exists is to try to snag the carry flag before later ADDs in the loop control code change it. And the success of that scheme, of course, is highly dependent on your code generator. You'll be looking at the assembly output anyway; might as well just write the routine in assembler to start with.

    Another way to tackle the problem is to do the U8 addition in a U16, so you wind up with the carry bit in an actual C variable, instead of relying on quirks of the compiler and architecture:

    void U48AddEq (U8 data* dst, U8 data* src, U8 len)
        {
        U16 temp = 0;
    
        while (len--)
            {
            temp += dst[len] + src[len];
    	dst[len] = (U8)temp;
            temp >>= 8;
            }
        }
    
    

    The Keil compiler isn't always good at recognizing opportunities to do strength reduction on shifts by multiples of 8, though. Might have to descend to some hackery to get the high half of temp into the low half with one MOV. Of course, the pointer dereferencing and loop overhead are going to swamp the cost of the actual addition here in any case. Ultimately, you've got the problem that two 6-byte integers don't fit in 8 bytes of register.

    (I'm curious; why 6 bytes, and not 8? Ethernet MAC addresses? Not that you need to add those a lot.)