1

入力から読み取った整数 x と y を受け取る 2 つの関数があります。

製品返品 x * y

power は x ^ y を返しますが、再帰と積を使用してこれを計算します。したがって、x は「基数」、y は「指数」になります。

彼らは C++ から呼び出しました:

int a, b, x, y; 
a = product(x, y);
b = power(x, y);

そして、これがアスムです。私は製品を動作させましたが、それから製品を呼び出すための構文/方法/規則がわからないため(そして再帰のためにそれ自体を呼び出すため)、電力に問題があります。編集: 再帰を使用する必要があります。

    global product
    global power

    section .text

product:

    push  ebp       
    mov   ebp, esp  

    sub esp, 4  

    push edi    
    push esi    

    xor   eax, eax

    mov edi, [ebp+8]        
    mov esi, [ebp+12]   

    mov [ebp-4], edi        

product_loop:
    add [ebp-4], edi        
    mov eax, [ebp-4]                                    
    sub esi, 1          
    cmp esi, 1      
    jne product_loop        

product_done:
    pop esi         
    pop edi         
    mov esp, ebp    
    pop ebp         
    ret             

power:

    push  ebp       
    mov   ebp, esp  

    sub esp, 4  

    push edi    
    push esi    
    push ebx    

    xor   eax, eax  

    mov edi, [ebp+8]        
    mov esi, [ebp+12]       

    ;;;

check: 
    cmp   esi, 1            ; if exp < 1 
    jl  power_stop          

recursion:                  ; else (PLEASE HELP!!!!!!!!)
    ; eax = call product (base, (power(base, exp-1)) 

power_stop: 
    mov eax, 1              ; return 1 

power_done:
    push ebx        
    pop esi         
    pop edi         
    mov esp, ebp    
    pop ebp         
    ret     

編集:私の解決策!

power:
    ; Standard prologue
    push  ebp       ; Save the old base pointer
    mov   ebp, esp  ; Set new value of the base pointer

    sub esp, 4  ; make room for 1 local variable result

    push ebx    ; this is exp-1

    xor   eax, eax  ; Place zero in EAX. We will keep a running sum

            mov     eax, [ebp+12]           ; exp
            mov    ebx, [ebp+8]             ; base
            cmp     eax, 1                  ; n >= 1
            jge     L1                      ; if not, go do a recursive call
            mov     eax, 1                  ; otherwise return 1
            jmp     L2
    L1:
            dec     eax                     ; exp-1
            push    eax                     ; push argument 2: exp-1
            push    ebx                     ; push argument 1: base
            call    power                   ; do the call, result goes in eax: power(base, exp-1)
            add     esp, 8                  ; get rid of arguments
            push eax                        ; push argument 2: power(base, exponent-1)
            push ebx                        ; push argument 1: base
            call product                    ; product(base, power(base, exponent-1))
    L2:

    ; Standard epilogue

    pop ebx         ; restore register
    mov esp, ebp    ; deallocate local variables
    pop ebp         ; Restore the callers base pointer.
    ret             ; Return to the caller.
4

2 に答える 2

2

CDECL呼び出し規則を使用しているため、最初にスタック内の引数を逆方向にプッシュし、次に関数を呼び出して、リターン後にスタックをクリーンアップする必要があります。

         push   arg_last
         push   arg_first
         call   MyFunction
         add    esp, 8      ; the argument_count*argument_size

ただし、コードに関する注意事項を次に示します。

  1. 関数productは値を返しません。ラベルのmov eax, [ebp-4]直後に使用してください。product_done

  2. 乗算は命令mulorによって非常に簡単に実行できimulます。足し算を使用するのは、可能な限り遅い方法です。

  3. 再帰によるべき乗の計算は最良のアイデアではありません。次のアルゴリズムを使用します。

    1. Y = 1;

    2. N=0 の場合は終了します。

    3. N が奇数の場合 -> Y = Y*x; N=N-1

    4. N が偶数の場合 -> Y = Y*Y; N=N/2

    5. 2に行く

SHRN を 2 で割るには、命令を使用しますtest。奇数/偶数を調べるには、命令を使用します。

productこのように、関数から呼び出す必要はありませんpower

于 2013-11-05T10:07:04.393 に答える
1

アセンブリの記述方法がわからない場合は、通常、C++ で記述し、手がかりとしてアセンブルできます。次のようにします。

int power(int n, int exp)
{
    return exp == 0 ? 1 :
           exp == 1 ? n :
           product(n, power(n, exp - 1));
}

次にgcc -S、またはアセンブリ出力用のコンパイラの同等のスイッチを使用するか、必要に応じてマシンコードを逆アセンブルできるようにする必要があります。

たとえば、Microsoft のコンパイラ ala でコンパイルされた上記の関数は、 int product(int x, int y) { return x * y; }andでスローされます。int main() { return product(3, 4); }cl /Fa power.cc

; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00.30729.01

        TITLE   C:\home\anthony\user\dev\power.cc
        .686P
        .XMM
        include listing.inc
        .model  flat

INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES

PUBLIC  ?product@@YAHHH@Z                               ; product
; Function compile flags: /Odtp
_TEXT   SEGMENT
_x$ = 8                                                 ; size = 4
_y$ = 12                                                ; size = 4
?product@@YAHHH@Z PROC                                  ; product
; File c:\home\anthony\user\dev\power.cc
; Line 1
        push    ebp
        mov     ebp, esp
        mov     eax, DWORD PTR _x$[ebp]
        imul    eax, DWORD PTR _y$[ebp]
        pop     ebp
        ret     0
?product@@YAHHH@Z ENDP                                  ; product
_TEXT   ENDS
PUBLIC  ?power@@YAHHH@Z                                 ; power
; Function compile flags: /Odtp
_TEXT   SEGMENT
tv73 = -8                                               ; size = 4
tv74 = -4                                               ; size = 4
_n$ = 8                                                 ; size = 4
_exp$ = 12                                              ; size = 4
?power@@YAHHH@Z PROC                                    ; power
; Line 4
        push    ebp
        mov     ebp, esp
        sub     esp, 8
; Line 7
        cmp     DWORD PTR _exp$[ebp], 0
        jne     SHORT $LN5@power
        mov     DWORD PTR tv74[ebp], 1
        jmp     SHORT $LN6@power
$LN5@power:
        cmp     DWORD PTR _exp$[ebp], 1
        jne     SHORT $LN3@power
        mov     eax, DWORD PTR _n$[ebp]
        mov     DWORD PTR tv73[ebp], eax
        jmp     SHORT $LN4@power
$LN3@power:
        mov     ecx, DWORD PTR _exp$[ebp]
        sub     ecx, 1
        push    ecx
        mov     edx, DWORD PTR _n$[ebp]
        push    edx
        call    ?power@@YAHHH@Z                         ; power
        add     esp, 8
        push    eax
        mov     eax, DWORD PTR _n$[ebp]
        push    eax
        call    ?product@@YAHHH@Z                       ; product
        add     esp, 8
        mov     DWORD PTR tv73[ebp], eax
$LN4@power:
        mov     ecx, DWORD PTR tv73[ebp]
        mov     DWORD PTR tv74[ebp], ecx
$LN6@power:
        mov     eax, DWORD PTR tv74[ebp]
; Line 8
        mov     esp, ebp
        pop     ebp
        ret     0
?power@@YAHHH@Z ENDP                                    ; power
_TEXT   ENDS
PUBLIC  _main
; Function compile flags: /Odtp
_TEXT   SEGMENT
_main   PROC
; Line 11
        push    ebp
        mov     ebp, esp
; Line 12
        push    4
        push    3
        call    ?power@@YAHHH@Z                         ; power
        add     esp, 8
; Line 13
        pop     ebp
        ret     0
_main   ENDP
_TEXT   ENDS
END

これについて説明するには:

?power@@YAHHH@Z PROC                                    ; power
; Line 4
        push    ebp
        mov     ebp, esp
        sub     esp, 8

上記はべき乗関数のエントリ コードです。関数の引数をジャンプするようにスタック ポインターを調整するだけで、以下では (つまり ) および (つまり ) として_exp$[ebp]アクセスexp_n$[ebp]ますn

; Line 7
        cmp     DWORD PTR _exp$[ebp], 0
        jne     SHORT $LN5@power
        mov     DWORD PTR tv74[ebp], 1
        jmp     SHORT $LN6@power

基本的に、expが 0 でない場合は下のラベルに進みますが、 0 の場合は のスタックの戻り値の場所に$LN5@powerロードし、 の関数 return 命令にジャンプします。1tv74[ebp]$LN6@power

$LN5@power:
        cmp     DWORD PTR _exp$[ebp], 1
        jne     SHORT $LN3@power
        mov     eax, DWORD PTR _n$[ebp]
        mov     DWORD PTR tv73[ebp], eax
        jmp     SHORT $LN4@power

上記と同様に、exp が 1 の場合、n を eax に入れ、そこから戻り値スタック メモリに入れ、return 命令にジャンプします。

今、それは面白くなり始めています...

$LN3@power:
        mov     ecx, DWORD PTR _exp$[ebp]
        sub     ecx, 1
        push    ecx

exp から 1 を引き、スタックにプッシュします...

        mov     edx, DWORD PTR _n$[ebp]
        push    edx

n もスタックにプッシュします...

        call    ?power@@YAHHH@Z                         ; power

上記の 2 つの値プッシュを使用する power 関数を再帰的に呼び出します。

        add     esp, 8

上記の関数が戻った後のスタック調整。

        push    eax

再帰呼び出しの結果 (パワーリターン命令が eax レジスタに残したもの) をスタックに置きます...

        mov     eax, DWORD PTR _n$[ebp]
        push    eax

n もスタックにプッシュします...

        call    ?product@@YAHHH@Z                       ; product

product 関数を呼び出して、power上記の呼び出しによって返された値を で乗算しnます。

        add     esp, 8
        mov     DWORD PTR tv73[ebp], eax

productの結果をスタック上の一時アドレスにコピーします....

$LN4@power:
        mov     ecx, DWORD PTR tv73[ebp]
        mov     DWORD PTR tv74[ebp], ecx

tv73 の一時的な場所から値を取得し、tv74 にコピーします...

$LN6@power:
        mov     eax, DWORD PTR tv74[ebp]

最後に、product()tv74 からの結果を eax レジスタに移動して、product呼び出しが返された後に便利で高速なアクセスができるようにします。

; Line 8
        mov     esp, ebp
        pop     ebp
        ret     0

スタックをクリーンアップして戻ります。

于 2013-11-05T10:25:54.327 に答える