Основы программирования

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


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

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). Эти команды, как правило, присутствуют в наборе инструкций любого процессора.


Содержание раздела