0

I am trying to write a recursive combination function in assembly (Yasm (similar to nsam)). I cannot use loops, multiplication or division.

I certain I am on the right track but am having issues once I hit the second inner function call. Can anyone help me and tell me where I am going wrong?

Edit: This is my updated code which returns a result but is not always correct. I think I must have a small bit of logic incorrect.

    mov     rax, [n]
    push    rax
    mov     rax, [k]
    push    rax
    call    func    
    ...     ...     program continues from here

 func:                      
    push    rbp 
    mov     rbp, rsp

    push    rdi
    push    rsi

    cmp     rsi, 0
    je      stopcond
    cmp     rdi, rsi
    jne     contin

stopcond:
    mov     rax, 1
    jmp     endfunc
contin:
    ;C(n-1,k-1)
    mov     rax, [rsp]  ; This is k
    dec     rax
    mov     rdx, rax
    mov     rax, [rsp+8]  ; This is n
    dec     rax
    mov     rsi, rdx
    mov     rdi, rax
    call    func

    mov     rbx, rax
    mov     rax, [rsp+8]  ; This is n
    dec     rax
    mov     rdx, rax
    mov     rax, [rsp]  ; This is k
    mov     rsi, rax
    mov     rdi, rdx
    call    func

    add     rax, rbx

endfunc:
    add     rsp, 16
    pop     rbp
    ret

This is a javascript implementation I have been using as a reference

function(n,k) {
    if ( k==0 || k==n ) {
        return 1
    } else {
        return C(n-1,k-1) + C(n-1,k)
    }
}
MPelletier
  • 15,130
  • 14
  • 79
  • 128
ojhawkins
  • 3,052
  • 14
  • 44
  • 67
  • If that's all of your code you're going to have issues when `func` eventually returns, since `func:` directly follows `call func`. – Michael May 29 '14 at 11:06
  • That's what I thought but when I look at c->assembly conversion of this `int func(int n,int k){if(k==0||k==n){return 1;}else{return func(n-1,k-1)+func(n-1,k);}}` here http://assembly.ynh.io/ they do a very similar thing @Michael – ojhawkins May 29 '14 at 11:13
  • That C code does not include the initial call to `func`. – Michael May 29 '14 at 11:19
  • This is it here, the only interesting thing is it puts `n` & `k` into `edi` & `esi` respectively `int func(int n,int k){if(k==0||k==n){return 1;}else{return func(n-1,k-1)+func(n-1,k);}}int main(){int a=func(3,2);return 0;}` – ojhawkins May 29 '14 at 11:24
  • Some important things to note if you're looking at the assembly output at that website is that A) the call from `main` to `func` is located _after_ the body of `func`, not before it like in your code, and that B) after `func` returns to `main` there's a `ret` to return back to the C runtime that call the `main` function. – Michael May 29 '14 at 11:42
  • @Michael, sorry I misread your first question. No this is no all the code, just the bare essentials to show the function call. – ojhawkins May 29 '14 at 12:00

1 Answers1

2
  1. The C(n-1,k) invocation is incorrect, because both rax and rbx have already been decremented and the recursive invocation destroys their value anyway. The simple fix is to reload the arguments from the stack.
  2. Similarly, the recursive invocation will use rdx for its own temporary, thereby overwriting the caller's copy. Solution: you should allocate a local variable on the stack, and use that as temporary storage.
  3. You are not restoring the stack pointer. At endfunc you should insert a mov esp, ebp.
  4. You are not following the usual calling convention. This is not a problem if you agree with the caller, but might still be a good idea to do so. You can get a quick overview at wikipedia.

PS: The usual advice applies: learn to use a debugger so you can fix your own mistakes.

Jester
  • 52,795
  • 4
  • 67
  • 108
  • Brilliant. I am looking at the assembly output for a C equivalent program and can see certain conventions being used. – ojhawkins May 29 '14 at 11:02