3

I have the following method in C that takes two 16-bit short ints and:

  • Adds the two integers
  • If the carry flag is set, add 1 to the result
  • Negate (NOT) all the bits in the final results
  • Return the result:

    short __declspec(naked) getchecksum(short s1, short s2)
    {
        __asm
        {
            mov ax, word ptr [esp+4]
            mov bx, word ptr [esp+8]
            add ax, bx
            jnc skip_add
            add ax, 1
            skip_add:
            not ax      
            ret
        }
    }
    

I had to write this in inline assembly because I do not know any way to test the carry flag without using assembler. Does anyone know of a way to do this?

Govind Parmar
  • 20,656
  • 7
  • 53
  • 85
  • 1
    Although not an answer to your question couldn't you use `adc ax, 0` and remove `jnc skip_add` , `add ax, 1`, `skip_add:` . `adc ax, 0` would add `0` to `ax` and then add 1 to that result if the carry flag is set. Another concern with the code is that `bx` is altered and the `cdecl` calling conventions require `bx` to be preserved by the called function (ie. `push/pop bx`) or use a register that doesn't need to be preserved by function (like `cx` or `dx`). You could write `add ax, bx` to eliminate need for `bx` with `add ax, word ptr [esp+8]` and remove `mov` to `bx` altogether – Michael Petch Nov 07 '14 at 18:35

2 Answers2

5

No (C has no notion of flags at all) but that doesn't mean you can't get the same result. If you use 32bit integers to do addition, the 17th bit is the carry. So you can write it like this:

uint16_t getchecksum(uint16_t s1, uint16_t s2)
{
    uint32_t u1 = s1, u2 = s2;
    uint32_t sum = u1 + u2;
    sum += sum >> 16;
    return ~sum;
}

I've made the types unsigned to prevent trouble. That may not be necessary on your platform.

harold
  • 61,398
  • 6
  • 86
  • 164
3

You don't need to access the flags to do higher precision arithmetics. There is a carry if the sum is smaller than either of the operands, so you can do like this

short __declspec(naked) getchecksum(short s1, short s2)
{
    short s = s1 + s2;
    if ((unsigned short)s < (unsigned short)s1)
        s++;
    return ~s;
}

There are already many questions about adding and carrying on SO: Efficient 128-bit addition using carry flag, Multiword addition in C

However in C operations are always done at least in int type so you can simply add that if int has more than 16 bits in your system. In your case the inline assembly is 16-bit x86 so I guess you're on Turbo C which should be get rid ASAP (reason: Why not to use Turbo C++?). In other systems that has 16-bit int you can use long which is guaranteed to be at least 32 bits by the standard

short __declspec(naked) getchecksum(short s1, short s2)
{
    long s = s1 + s2;
    return ~((s & 0xffff) + ((s >> 16) & 0x1));
}
phuclv
  • 37,963
  • 15
  • 156
  • 475
  • Couldn't `s >> 16` actually give you -1 there? – harold Nov 07 '14 at 18:37
  • 1
    @harold strictly speaking it's implementation defined, but yes, it'll most probably become -1 if the sum is negative. Fixed – phuclv Nov 07 '14 at 18:55
  • It's actually MSVC - I'm using the 32-bit stack pointer `esp` but 16-bit GP registers because the shorts are 16-bits wide – Govind Parmar Nov 07 '14 at 19:19
  • 1
    @GovindParmar you really shouldn't use 16bit code just because you have 16bit data, it causes trouble, zero extend your data when necessary and work in 32bit – harold Nov 07 '14 at 19:24