2016-10-26 29 views
2

アセンブリ言語の配列の平均、最小、最大を見つける際に問題があります。私はC++で単純な配列を作成し、それを渡すためのtest.asmファイルを作成しました。私は平均を考え出しましたが、今ではそれが分かりません。アセンブリの平均、最小、最大を見つける

#include <iostream> 
using namespace std; 

extern "C" 
int test(int*, int); 

int main() 
{ 
const int SIZE = 7; 
int arr[SIZE] = { 1,2,3,4,5,6,7 }; 

int val = test(arr, SIZE); 


cout << "The function test returned: " << val << endl; 

return 0; 
} 

これは、最大が同様に行われることになるので、私はまだ分を見つける方法を把握しようとしていますすべての値を返す4.

.686 
.model flat 

.code 


_test PROC ;named _test because C automatically prepends an underscode, it is needed to interoperate 
push ebp 
mov ebp,esp ;stack pointer to ebp 

mov ebx,[ebp+8] ; address of first array element 
mov ecx,[ebp+12] 
mov ebp,0 
mov edx,0 
mov eax,0 


loopMe: 
cmp ebp,ecx 
je allDone 


add eax,[ebx+edx] 
add edx,4 
add ebp,1 
jmp loopMe 

allDone: 
mov edx,0 
div ecx 

pop ebp 
ret 
_test ENDP 

END 

を追加私のTEST.ASMです方法。私はあなたが値を比較するためにcmpを使用すると仮定しますが、これまでに試したことはすべて成功しませんでした。私はアセンブリ言語をかなり新しく理解しています。どんな助けもありがとうございます。

+2

代わりにCで記述し、コンパイラの出力を見て、それが何であるかを確認してください。次に、実際にアセンブリに書き込むかどうかを決定します。 –

+3

最小/最大を保持するレジスタを選択し、最初の配列要素で初期化します。次に、 'cmp'命令を使って、その値を現在の要素に置き換えるかどうかを決定します。シンプル。 –

+0

最小、最大、平均を呼び出しプロセスに戻しますか? 1つの32ビットレジスタで値が255未満であれば、これを行うことができますが、それ以上の値には追加ロジックが必要です。 –

答えて

1

すべてのヘルプは、[OK]を

を高く評価しているので、私は、あなたが直接それを要求していない場合でも、平均機能をリファクタリング表示されます。 :)あなたはこのから学ぶことができる

もの:ebpがコード

  • で修正されていない場合

    • 簡略化された機能プロローグ/エピローグ、入力配列は32B int値であるので、正確な平均にあなたを持っています64Bの和を計算し、ゼロ値を取得する方法の微妙な「トリック」を
    • 64B和符号付き除算を行う(xor)または方法incがZを取り扱う値に+1(コードサイズを低下させる)
    • であるべきですERO偽平均0(非クラッシュ)
    • 32Bレジスタからなる二64B値の
    • 添加/命令
    • 人間の「インデックス」をカウントを返さないことにより、サイズの配列(+1 =>size可能と直接CMP)、まだ32B値をアドレッシング(アドレッシングで*4の使用)getAverage

    に改名

  • はところで、これはパフォーマンスのために最適化されていない、私は「単純な」ソースを維持しようとしたので、読んで、それをやっているかを理解するのは簡単です。

    _getAverage PROC 
        ; avoiding `ebp` usage, so no need to save/set it 
        mov ebx,[esp+4] ; address of first array element 
        mov ecx,[esp+8] ; size of array 
        xor esi,esi  ; array index 0 
        ; 64b sum (edx:eax) = 0 
        xor eax,eax 
        cdq 
        ; test for invalid input (zero sized array) 
        jecxz zeroSizeArray ; arguments validation, returns 0 for 0 size 
    
        ; here "0 < size", so no "index < size" test needed for first element 
        ; "do { ... } while(index < size);" loop variant 
    
    sumLoop: 
        ; extend value from array[esi] to 64b (edi is upper 32b) 
        mov edi,[ebx+esi*4] 
        sar edi,31 
        ; edx:eax += edi:array[esi] (64b array value added to 64b sum) 
        add eax,[ebx+esi*4] 
        adc edx,edi 
        ; next index and loop while index < size 
        inc esi 
        cmp esi,ecx 
        jb sumLoop 
    
        ; divide the 64b sum of integers by "size" to get average value 
        idiv ecx   ; signed (!) division (input array is signed "int") 
        ; can't overflow (Divide-error), as the sum value was accumulated 
        ; from 32b values only, so EAX contains full correct result 
    zeroSizeArray: 
        ret 
    _getAverage ENDP 
    
  • +1

    オーバーフローの危険なしにidivを使うことができるといい考えです。私はループ内でCDQを使用していたかもしれません(各配列要素をEAXに一度だけロードする)が、ループの後に2つのMOV命令が必要です。同じ場所への2つのメモリ参照はここでは悪くありません(読みやすさやわかりやすさが損なわれるとは思いません。もちろん、スピードに配慮すれば、SSE4 PMOVZXの後にPADDQを使用するか、SSE2(おそらくPSRADとPUNPCKL/HDQを元にして)で手動で署名拡張します。 –

    関連する問題