2016-10-26 4 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; 
} 

Это мой test.asm, который добавляет все значения и возвращает 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 

Я все еще пытаюсь выяснить, как найти мин, так как максимум будет сделано в подобном путь. Я предполагаю, что вы используете cmp для сравнения значений, но все, что я пробовал до сих пор, не было успешным. Я довольно новичок в ассемблере, и мне трудно понять. Любая помощь приветствуется.

+2

Предлагайте записать его на C вместо этого и посмотреть на вывод компилятора, чтобы увидеть, что он делает. Затем решите, действительно ли хотите писать в сборке. –

+3

Выберите регистр, чтобы сохранить min/max и инициализировать его с помощью первого элемента массива. Затем используйте команду 'cmp', чтобы определить, следует ли заменить это значение на текущий элемент или нет. Просто. –

+0

Вы хотите вернуть минимальный, максимальный и средний к вызывающему процессу? Это можно сделать, если значения меньше 255 в одном 32-битном регистре, но для значений, превышающих это, потребуется дополнительная логика. –

ответ

1

Любая помощь приветствуется

Итак, я покажу вам реструктурировать среднюю функцию, даже если вы не просите об этом прямо. :)

Вещи, которые вы можете узнать из этого:

  • упрощена функция пролог/эпилог, когда ebp не изменяются в коде
  • массив ввода имеет 32b int значений, так, чтобы иметь правильное среднее вы следует рассчитать 64b сумму, и сделайте 64b сумму знакового деления
  • тонкие «трюки», как получить нулевое значение (xor) или как inc это +1 к значению (снижение размера кода)
  • обработки г эро размер массив с помощью не возвращаются поддельные средние 0 (без сбоев)
  • добавления двух 64b значений, состоящих из 32b регистров/инструкции
  • подсчет человеческого «индекса» (+-=> прямой CMP с size возможно), все же решением 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), но после цикла потребуются две команды MOV. Здесь не так плохо написано, что в двух местах памяти не так плохо (я не думаю, что это больно читаемости или понятности, а именно то, что вы собираетесь делать). Разумеется, если бы я заботился о скорости, я бы использовал PADDQ после SSE4 PMOVZX или вручную расширился с помощью SSE2 (возможно, PSRAD и PUNPCKL/HDQ с оригиналом). –