Мониторинг обменных курсов валют
18c0693f

Суммирование массива


Рассмотрим еще один простой пример программы на Ассемблере. Требуется вычислить сумму элементов целочисленного массива заданной длины. Прототип этой функции на языке Си выглядит следующим образом:

int sum(int a[], int n);

Функции передается (как обычно, через аппаратный стек) адрес начала целочисленного массива a и его длина n. На RTL функция sum записывается следующим образом:

// Вход в функцию: push FP; // сохраним старое значение FP; FP := SP; // определим новое значение FP; push R1; // сохраним значения регистров R1, push R2; // R2 push R3; // и R3. // R0 := 0; // обнулим сумму R1 := m[FP+8]; // R1 := a (адрес начала массива) R2 := m[FP+12]; // R2 := n (число эл-тов массива) L1: // метка начала цикла CC0 := R2 - 0; // сравнить R2 с нулем if (le) goto L2; // если результат ‹ = 0, // то перейти на метку L2 R3 := m[R1]; // R3 := очередной элемент массива R0 := R0 + R3; // прибавим очередной эл-т к сумме R1 := R1 + 4; // увеличим адрес очер. эл-та на 4 R2 := R2 - 1; // уменьшим счетчик необр. эл-тов goto L1 // перейти на метку L1 L2: // метка конца цикла // ответ уже содержится в R0 // выход из функции: pop R3; // восстановим значения R3, pop R2; // R2 pop R1; // и R1 pop FP; // восстановим старое значение FP return; // вернемся в вызывающую программу

В этой программе адрес очередного элемента массива содержится в регистре R1. Сумма просмотренных элементов массива накапливается в регистре R0. Регистр R2 содержит число еще не обработанных элементов массива. В начале программы в регистр R1 записывается адрес начала массива, а в R2—число элементов массива. В теле цикла очередной элемент массива читается из памяти и помещается в регистр R3, затем содержимое R3 прибавляется к сумме R0. После каждого выполнения тела цикла адрес очередного элемента увеличивается на 4 (т.к. целое число занимает 4 байта), а количество необработанных элементов уменьшается на единицу. Цикл продолжается, пока содержимое регистра R2 (т.е. число необработанных элементов) больше нуля.

Для переписывания программы на Ассемблер Intel 80386 зафиксируем следующее распределение виртуальных регистров:


R0 — EAX R1 — EBX R2 — ECX R3 — EDX FP — EBP SP — ESP

Программа переписывается таким образом:

.386 .model flat, stdcall .code

sum: ; Вход в функцию: push EBP ; сохраним старое значение EBP mov EBP, ESP ; определим новое значение EBP push EBX ; сохраним значения регистров EBX, push ECX ; ECX push EDX ; и EDX. ; mov EAX, 0 ; EAX := 0 mov EBX, [EBP+8] ; EAX := a mov ECX, [EBP+12]; ECX := n L1: ; метка начала цикла cmp ECX, 0 ; сравнить ECX с нулем jle L2 ; если результат ‹ = 0, ; то перейти на метку L2 mov EDX, [EBX] ; EDX := очередной эл-т массива add EAX, EDX ; EAX := EAX+EDX add EBX, 4 ; EBX := EBX+4 (адрес след. эл-та) dec ECX ; ECX := ECX-1 (счетчик) jmp L1 ; перейти на метку L1 L2: ; метка конца цикла ; ответ содержится в регистре EAX ; выход из функции: pop EDX ; восстановим значения EDX, pop ECX ; ECX pop EBX ; и EBX. pop EBP ; восстановим значение EBP ret ; вернемся в вызывающую программу

public sum end

Отметим, что мы использовали команду уменьшения значения регистра на единицу dec (от слова decrement) для реализации следующей строки RTL:

R2 := R2 - 1; // уменьшим счетчик необр. эл-тов

В Ассемблере Intel 80386 она записывается как

dec ECX; ECX := ECX-1

Команда увеличения регистра на единицу обычно записывается как inc (от слова increment). Эти команды, как правило, присутствуют в наборе инструкций любого процессора.





Самый выгодный курс обмена валюты