2016-10-26 5 views
2

Ich habe ein Problem bei der Suche nach dem Durchschnitt, Minimum und Maximum eines Arrays in Assemblersprache. Ich habe ein einfaches Array mit C++ erstellt und eine test.asm-Datei erstellt, um sie zu übergeben. Ich habe den Durchschnitt herausgefunden, aber jetzt ist es das Minimum und Maximum, das ich anscheinend nicht herausfinden kann.Durchschnitt, Min und Max in Assembly finden

#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; 
} 

Das ist mein test.asm, dass die Werte alles addiert und liefert 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 

ich immer noch herauszufinden, wie ich versuche, die min zu finden, da wird der max in einem ähnlichen erfolgen Weg. Ich nehme an, Sie verwenden die cmp, um Werte zu vergleichen, aber alles, was ich bisher versucht habe, war nicht erfolgreich. Ich bin ziemlich neu in Assembler Sprache und es ist schwer für mich zu begreifen. Jede Hilfe wird geschätzt.

+2

Schlage vor, es in C zu schreiben, und schaue auf die Compiler-Ausgabe, um zu sehen, was es tut. Entscheiden Sie dann, ob Sie tatsächlich in Assembly schreiben möchten. –

+3

Wählen Sie ein Register, um Ihre Min/Max zu halten, und initialisieren Sie es mit dem ersten Array-Element. Verwenden Sie dann den Befehl "cmp", um zu bestimmen, ob dieser Wert durch das aktuelle Element ersetzt werden soll oder nicht. Einfach. –

+0

Möchten Sie Minimum, Maximum und Durchschnitt zum Aufrufprozess zurückgeben? Dies kann gemacht werden, solange Werte in einem 32-Bit-Register kleiner als 255 sind, aber Werte, die größer als das sind, würden zusätzliche Logik benötigen. –

Antwort

1

Jede Hilfe sehr geschätzt wird

Ok, also werde ich Ihnen zeigen, durchschnittliche Funktion Refactoring, auch wenn Sie nicht direkt für sie fragte. :)

Dinge, die Sie daraus lernen können:

  • vereinfachte Funktion Prolog/Epilog, wenn ebp nicht in Code geändert wird
  • das Eingangsarray von 32b int Werte, so dass Sie richtig Durchschnitt haben sollte 64b Summe, und führen Sie die 64b Summe unterzeichnet Division
  • subtil „Tricks“, wie Null-Wert zu erhalten (xor) oder wie inc +1 zu Wert (Senkung Codegröße)
  • Handhabung z berechnen ero Größe Array von gefälschten durchschnittliche 0 (kein Absturz) zurückkehr
  • Addition zweier 64b Werten zusammengesetzt aus Registern 32b/Anweisungen
  • Zählen menschlichen "index" (+1 => direkte cmp mit size möglich), aber Adressierung 32b Werte (Nutzung von *4 in Adressierung)
  • umbenannt getAverage

BTW, ist dies nicht für die Leistung optimiert ist, habe ich versucht, die Quelle „einfach“ zu halten, so ist es leicht zu lesen und zu verstehen, was es tut.

_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

Schön, gut zu denken, dass Sie idiv ohne Risiko des Überlaufs verwenden können. Ich könnte CDQ innerhalb der Schleife verwendet haben (um jedes Array-Element nur einmal in EAX zu laden), aber das würde zwei MOV-Anweisungen nach der Schleife erfordern. Zwei Speicherreferenzen am selben Ort sind hier nicht schlecht (ich glaube nicht, dass es Lesbarkeit oder Verständlichkeit schmerzt, für die du dich entscheidest). Natürlich würde ich PADDQ nach SSE4 PMOVZX verwenden oder manuell mit SSE2 (wahrscheinlich PSRAD und PUNPCKL/HDQ mit dem Original) erweitern. –