12

I'm an occasional C programmer. I've come across this bit of inline assembly code in a Turbo C program

#define ADC(dst,src)    { asm MOV AX, dst;  asm ADD AX, src;  \
              asm ADC AX, 0;    asm MOV dst, AX; }

dst and src are both unsigned 16-bit integers.

It won't compile in GNU C++. Could someone please explain what it's doing? TIA!

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Jim C
  • 165
  • 8
  • Nothing that makes obvious sense ... it's adding the carry back to the result for whatever reason. Something like `dst += src; if (dst < src) dst++;` – Jester Sep 07 '19 at 14:53
  • 1
    Am I right in thinking it would be something like this? AX= dst+ src; if((long)dst + (long)src > 0xffff) AX++; dst= AX; – Jim C Sep 07 '19 at 15:00
  • I don't think gcc accepts that syntax for inline assembly. Also did you actually mean to write `GNU C++` or rather `GNU C`? – walnut Sep 07 '19 at 15:01
  • Yes you could make something along those lines too. – Jester Sep 07 '19 at 15:02
  • I'm using GNU c++ but it's being compiled as c – Jim C Sep 07 '19 at 15:06
  • Inline assembly syntax is compiler specific because it is _not C_, so not defined by the language. Also being a "occasional C programmer" is irrelevant, because again, this is _not C_. Moreover Turbo C is an antique 16 bit compiler, so the assembler here is 16bit x86, which the 32 or 64 bit GCC will reject. – Clifford Sep 07 '19 at 15:13
  • 1
    @Clifford note that all these assembler instructions can be assembled for 32 and 64- bit mode though – Antti Haapala -- Слава Україні Sep 07 '19 at 16:13
  • 4
    We see here why I suggest people [not use inline asm](https://gcc.gnu.org/wiki/DontUseInlineAsm). (Among other things) it presents challenges (exacerbated by the lack of comments) to people who come along later to maintain it. "Occasional C programmers" are relatively common. Asm programmers? Not so much. Since obviously this is someone else's code, I'm not blaming you. But as NPE shows, it wasn't necessary in this case. – David Wohlferd Sep 07 '19 at 20:16
  • Thanks for the replies. Got the software going so am a happy man. – Jim C Sep 07 '19 at 22:28
  • @Clifford An experienced C programmer would have been sure it wasn't C and known to look elsewhere. An occasional C programmer very well might not. Combine that with an awful lot of programmers these days not knowing assembly and this could be very cryptic. – Loren Pechtel Sep 08 '19 at 04:31
  • 1
    Same as [described here](https://stackoverflow.com/questions/44540078/adc-instruction-in-asm/44540326#44540326), except with 16-bit operands/values, instead of 8-bit. – Cody Gray - on strike Sep 08 '19 at 07:01

2 Answers2

13

The first two instructions add dst and src together, storing the result in the accumulator. The third instruction computes what's sometimes called the "end-around carry" and the fourth stores the result in dst.

The following is an equivalent C implementation:

int32_t sum = dst + (int32_t)src;
dst = (int16_t)((sum & 0xffff) + (sum >> 16));

Wikipedia talks about end-around carry in its coverage of ones' complement:

To add two numbers represented in this system, one does a conventional binary addition, but it is then necessary to do an end-around carry: that is, add any resulting carry back into the resulting sum.

End-around carry is used, for example, when calculating IPv4 checksums.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
NPE
  • 486,780
  • 108
  • 951
  • 1,012
7

A literal translation of this code to GNU C inline assembly is:

static inline short ADC(short dst, short src)
{
     asm ("add %1, %0; adc $0, %0" : "+r"(dst) : "rmi"(src));

     return (dst);
}

But the version provided by NPE should be a bit more portable.

fuz
  • 88,405
  • 25
  • 200
  • 352