HLA Assembly Recursive Fibonacci Program

2019-10-06 17:59发布

问题:

I have written some code to solve this prompt:

Create an HLA Assembly language program that prompts for a number from the user. Create and call a function that calculates a value in the Fibonacci sequence. In mathematics, the Fibonacci sequence is named after the Italian mathematician Leonardo of Pisa who was known during his lifetime as Fibonacci. The Fibonacci sequence starts with 1 and 1. Each later term in the sequence is the sum of the two previous values. So the series will be: 1,1,2,3,5,8,13 and so on. In order to receive full credit, you must use recursion to solve this problem building a function whose signature is:

procedure fibRec( value : int8 ); @nodisplay; @noframe; Here are some example program dialogues to guide your efforts:

Provide a number: 3 fib(3) = 2

Provide a letter: 5 fib(5) = 5

In an effort to help you focus on building an Assembly program, I’d like to offer you the following C statements which match the program specifications stated above. If you like, use them as the basis for building your Assembly program.

SAMPLE C CODE:
------------------------
int main( )
{
  int value;
  printf( "Provide a value: " );
  scanf( "%d", &value );
  int f = fibRec( value );
  printf( "fib( %d ) = %d\n", value, f );
  return( 0 );
}

int fibRec( int value ) 
{
    int result = 1; 
    if (value == 1 || value == 2)   // base case
       result = 1;
    else 
       result = fibRec( value-1 ) + fibRec( value-2 );
    return( result );
}

and my approach is to try to use the C implementation and convert it to HLA. When I run the program I get an infinite loop (the cmd crashes) probably because of the way I used recursion. I'm not sure how to implement the

else result = fibRec( value-1 ) + fibRec( value-2 );

portion of the C implementation.

Here is what I have:

program fib;
#include("stdlib.hhf");

static
        value : int8; 
        //returnAddress : dword;
        //temp: int16;


procedure fibRec( value : int8 ); @nodisplay; @noframe; 

begin fibRec;

        mov(CL, value);
        mov(1, DL);

        cmp(CL, 1);
        je Res1;
        cmp(CL, 2);
        je Res1;

        jmp Else1;

        //else result = fibRec( value-1 ) + fibRec( value-2 );
Else1:

        //mov(1, DL);

        dec(CL);
        call fibRec;

        sub(2, CL);
        call fibRec;

        add(CL, DL);

        jmp ProgExit;

Res1:
        mov(1, DL);
        jmp ProgExit;

ProgExit:


end fibRec;


/////////////////////////////////////////////////////////////////////////////////////////////////////

begin fib;

    stdout.put( "Provide a value: " );
    stdin.get(value); //CHANGED TO IVALUE

    mov(CL, value); //SAVES THE INPUT TO A REGISTER


    call fibRec; // MUST CALL THE PROCEDURE
    stdout.put("fib(");
    stdout.puti8(value);
    stdout.put(") = ");
    stdout.put(DL);



end fib;

回答1:

Learn how to debug your code, there are obvious problems if you would try to step over it, like at the beginning you overwrite user input with value in CL.

Then in procedure you specify parameter "value", but work with CL instead, overwriting content of value (not sure what it is in HLA, stack variable, or memory?).

You use CL/DL 8 bit registers for values, but C example uses int (32b signed).

You use "@noframe":

The @NOFRAME option tells HLA that you don't want the compiler to automatically generate entry and exit code for the procedure. This tells HLA not to automatically generate the RET instruction (along with several other instructions).

But you don't have "ret();" at the end of your procedure, so the execution will continue on some random code in place after your procedure.

And finally about your recursion problem.

ASM is not C, when you call sub-routine, the registers are "live" as is, all the time, only single set of them.

So this is quite wrong:

    dec(CL);
    call fibRec;
    sub(2, CL);
    call fibRec;
    add(CL, DL);

After first call your CL and DL are already overwritten. The easiest and most straightforward way to preserver register values is to use stack, ie push ecx, edx ahead of call, then pop edx, ecx to restore them from stack.

For example, the fib. subroutine written in x86 32b assembler (NASM Intel syntax! So it's mov destination, source, the other way than your HLA!):

fibRecursion:
    ; expects unsigned "n" (1+) in eax, returns fibonacci(n) in eax
    ; will crash on large "n" due to stack overflow
    cmp   eax,2
    ja    moreThanTwo
    mov   eax,1         ; n: 0, 1 and 2 returns "1"
    ret
moreThanTwo:
    push  edx           ; preserve edx
    dec   eax
    push  eax           ; store n-1 in stack
    call  fibRecursion  ; eax = fib(n-1)
    xchg  eax,[esp]     ; store fib(n-1) in stack, get n-1 into eax
    dec   eax
    call  fibRecursion  ; eax = fib(n-2)
    pop   edx           ; edx = fib(n-1)
    add   eax,edx       ; eax = fib(n) = eax+edx
    pop   edx           ; restore edx
    ret

Yep, so now you have just to fix the syntax for HLA... (more like rewrite it, so you make sure you understand how it works).

And learn how to debug your code, I think I forgot to mention this.

Also did I mention you should debug your code?

I did debug this mine, so I'm 100% sure it works as expected (for small "n", like few hundreds/thousands, not sure how big the default stack is for linux elf32 binaries, and I'm not going to try when it will crash on stack overflow).