0

So I'm trying to sum 1 + 11 + 111 given a number, n, which determines how many values of the series I add together. i.e n = 2, I will add 1 + 11, or n = 3, I will add 1 + 11 + 111. I have written the function in C already, but I am trying to translate it into x86 assembly and I am having trouble. Here is the C function:

int summation(int n)
   {
    int sum = 0, j = 1;
    for (int i = 1; i <= n; i++)
    {
    sum = sum + j;

    // Appending a 1 at the end
    j = (j * 10) + 1;
   }

    return sum;

Here is my x86 assembly code:

unsigned int seriesSum(int n)
{
unsigned int sum=0;

__asm 
{
   mov ebx, n //input n
   mov eax, 1
   mov ecx, 10
   mov edx, 0



   Begin:
   cmp ebx, 0 // determines end of loop or not
   je EndOfWhile
       add edx, edx
       add edx, eax
       mul ecx
       add eax, eax
       inc eax
       dec ebx

       jmp Begin //Loop back

    EndOfWhile:
   mov sum, edx 


}
return sum;

I thought I have it translated correctly but I seem to be getting 0s as my sum.

Shawn S
  • 9
  • 2
  • Use godbolt.org to see the asm generated for your C function – James Feb 17 '18 at 21:12
  • I tried that but when I try to copy what godbolt gives me into my inline asm area, I get tons of improper operand errors. Like mov rbp, rsp which is essentially the first line, doesn't even work. edit: I actually got all the errors to go away, just mov rbp, rsp is the only one remaining. Getting an improper operand type error. Any idea how to fix? – Shawn S Feb 17 '18 at 21:30
  • 1
    Use `-m32` to compile for 32-bit. Obviously you only copy the instructions you need, not including the instructions to make a stack frame which MSVC will already emit for you outside your inline-asm. And use `-Os` to optimize for small code instead of taking the ugly un-optimized compiler output. – Peter Cordes Feb 17 '18 at 23:15
  • Single-stepping this with a debugger would have shown you where edx is clobbered. Also, does that series have a closed-form solution? It's not as simple as `n * (n+1) / 2` for the sum of 1..n, because the terms grow approximately exponentially, not linearly. – Peter Cordes Feb 17 '18 at 23:21

2 Answers2

2

You're using edx to hold your sum, but the mul ecx instruction puts the upper word of the result into edx clobbering it.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • Ah, okay. I thought that the mul ecx instruction was going to change eax. thank you. – Shawn S Feb 17 '18 at 21:26
  • 1
    @ShawnS: `mul ecx` changes both `eax` and `edx`: multiplying two 32 bit values gives a (maximum) 64 bit result, in `edx:eax`, where `edx` contains the higher 32 bits. – Rudy Velthuis Feb 17 '18 at 23:08
1

Use imul eax, ecx to just do eax *= ecx without storing the high half anywhere. (One-operand imul is a full multiply that stores the result in edx:eax.)

Or even better, eax = eax*10 +1 in x86 is best done with add eax,eax / lea eax, [eax+eax*4 + 1], not with mul or imul at all. Actually, optimizing for latency instead of code-size, you'd want to avoid the 3-component LEA by splitting it up as

lea  eax, [eax + eax*4]   ; eax *= 5
lea  eax, [1 + eax*2]     ; NOT  [ 1 + eax + eax ], which is shorter but slower
                          ; eax = orig_eax*5*2 + 1

NASM and YASM optimize that for code size into [ 1 + eax + eax*1 ] (so it can use a base + scaled-index + disp8 instead of disp32 + scaled-index). I'm not sure how to override the addressing mode; [dword 1 + eax*2] uses a disp32, but still splits the eax*2 into eax + eax*1, and strict eax*2 doesn't assemble. Hopefully an answer exists for How to force NASM to encode [1 + rax*2] as disp32 + index*2 instead of disp8 + base + index?

Obviously MASM will be different, but I don't have MASM available.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847