Wednesday, May 09, 2012

Assembly, day 2 - simple control flow

Using the same inline assembly framework as yesterday, today I want to use a simple loop. The example will be to sum all the numbers between 0 and n, where n is passed in as a parameter. To do this I will simulate a 'backward' for loop: for (; n > 0; --n), and in the body just add n to the running total. The function looks like:

int sum(unsigned int n)
{
  int result;

  __asm {
    mov eax, 0
    mov ebx, n
  start:
    add eax, ebx
    dec ebx
    cmp ebx, 0
    jg start
    mov result, eax
  }

  return result;
}

Here, we keep the running total in eax, and the current value of n in ebx. The first two mov instructions initialise these registers. Then the 'start' label identifies the beginning of the loop. In the loop we add the current 'n' to the running result, and decrement 'n'. The 'cmp' instruction compares its two arguments and sets some flags accordingly, the following instruction (jg) uses those flags and jumps if ebx is greater than zero. If we don't jump, then we are done and we can copy the result out of the register and into memory, to 'result', to be precise.

An Alternative:

  mov eax, 0
  mov ecx, n
start:
  add eax, ecx
  loopnz start
  mov result, eax

In the alternative we make use of the x86 loop instruction (the C stuff is exactly the same as the first version). Now we use ecx, rather than ebx, to store our counter ('n'). The loopnz instruction automatically decrements ecx (which is why we use it instead of ebx), and jumps to the given label if ecx is not equal to zero (n is always positive, so that is OK). This version uses fewer instructions, and is perhaps a little clearer. No idea which version would be quicker, modern processors are far too complex to make such a judgement without measuring.

No comments: