Лабораторная 7
.docxСанкт-Петербургский политехнический университет Петра Великого
Институт компьютерных наук и технологий
Высшая школа интеллектуальных систем и суперкомпьютерных технологий
ЛАБОРАТОРНАЯ РАБОТА №7
«Система команд, способы адресации и использование подпрограмм в архитектуре IntelArch-32»
по дисциплине «Архитектура вычислительных систем»
Выполнил
студент гр. 3530903/80001 А. В. Шильникова
Руководитель Н. М. Вербова
«___» __________ 2020 г.
Санкт-Петербург
2020
Цель работы: изучить ассемблерный код программы с вызовом подпрограммы.
Задачи:
1. Транслировать программу, написанную на С.
2. Перейти в режим отладки.
3. Проанализировать и прокомментировать все команды.
Вариант 6
Исходный код программы
//Третий параметр типа int* - адрес, по которому возвращает результат.
//В функции объявите и используйте локальную переменную типа long int.
void Fn(unsigned char A, char B, int* pointer)
{
long int li = 7;
while (li != 0)
{
A++;
B--;
li--;
}
*pointer = A + B;
}
int main()
{
unsigned char ucA = 4;
char c = 6;
int variable = 3;
int* pointer = &variable;
Fn(ucA, c, pointer);
return 0;
}
Дизассемблированный код с комментариями:
1: //Третий параметр типа int* - адрес, по которому возвращает результат.
2: //В функции объявите и используйте локальную переменную типа long int.
3:
4: void Fn(unsigned char A, char B, int* pointer)
5: {
009A1000 55 push ebp //сохраняем значение регистра ebp в стек, чтобы после выполнения программы или подпрограммы frame pointer можно было вернуть в состояние до вызова функции
009A1001 8B EC mov ebp,esp //в регистр ebp копируется значение из регистра esp
009A1003 83 EC 44 sub esp,44h //выделяем память для переменных
009A1006 53 push ebx
009A1007 56 push esi
009A1008 57 push edi //сохраняем в стек значения регистров ebx, esi, edi
6: long int li = 7;
009A1013 C7 45 FC 07 00 00 00 mov dword ptr [li],7 //записываем в локальную переменную li значение 7. dword ptr означает, что размер i составляет 32 бита, что соответствует размеру, занимаемому в памяти переменной типа long
7: while (li != 0)
009A101A 83 7D FC 00 cmp dword ptr [li],0 //устанваливаем флаг сравнения значения счетчика li со значением 0, то есть проверяем условие цикла while. dword ptr означает, что размер i составляет 32 бита, что соответствует размеру, занимаемому в памяти переменной типа long.
009A101E 74 1B je Fn+3Bh (09A103Bh) //условие сравнения – je (переход, если равно)
8: {
9: A++;
009A1020 8A 45 08 mov al,byte ptr [A] //заносим в al значение переменной A. byte ptr означает, что переменная имеет размер в один байт, что соответствует типу short
009A1023 04 01 add al,1 //прибавляем к al единицу
009A1025 88 45 08 mov byte ptr [A],al //заносим в переменную A значение регистра al
10: B--;
009A1028 8A 45 0C mov al,byte ptr [B] //заносим в al значение переменной B. byte ptr означает, что переменная имеет размер в один байт, что соответствует типу short
009A102B 2C 01 sub al,1 //вычитаем из al единицу
009A102D 88 45 0C mov byte ptr [B],al //заносим в переменную B значение регистра al
11: li--;
009A1030 8B 45 FC mov eax,dword ptr [li] //заносим в eax значение переменной li. dword ptr означает, что переменная имеет размер в 32 бита, что соответствует типу long
009A1033 83 E8 01 sub eax,1 //вычитаем из eax единицу
009A1036 89 45 FC mov dword ptr [li],eax //заносим в переменную li значение регистра eax
12: }
009A1039 EB DF jmp Fn+1Ah (09A101Ah) //возвращаемся к началу цикла и проверяем условие захода в него.
13: *pointer = A + B;
009A103B 0F B6 45 08 movzx eax,byte ptr [A] //в языке ассемблера существуют также команды, позволяющие занести в регистр значение другого регистра или ячейки памяти со знаковым или беззнаковым расширением. movzx - Беззнаковое расширение – старшие биты заполняются нулём. Запись в eax значение А
009A103F 0F BE 4D 0C movsx ecx,byte ptr [B] //movcx - Знаковое расширение – старшие биты заполняются знаковым битом. Запись в ecx значение B
009A1043 03 C1 add eax,ecx //складываются значения eax и ecx, сумма записываетс в eax
009A1045 8B 55 10 mov edx,dword ptr [pointer] //значение переменной pointer записывается в регистр edx
009A1048 89 02 mov dword ptr [edx],eax //значение регистра eax записывается в регистр edx
14: }
009A104A 5F pop edi
009A104B 5E pop esi
009A104C 5B pop ebx //получаем данные (edi, esi, ebx) из стека
009A104D 8B E5 mov esp,ebp //значение регистра ebp записывается в регистр esp
009A104F 5D pop ebp //получаем ebp из стека
009A1050 C3 ret //завершение выполнение программы
…
15:
16: int main()
17: {
009A1070 55 push ebp //сохраняем значение регистра ebp в стек, чтобы после выполнения программы или подпрограммы frame pointer можно было вернуть в состояние до вызова функции
009A1071 8B EC mov ebp,esp //в регистр ebp копируется значение из регистра esp
009A1073 83 EC 50 sub esp,50h //выделяем память для переменных
009A107B 33 C5 xor eax,ebp // обнулениe регистра eax
009A107D 89 45 FC mov dword ptr [ebp-4],eax //записываем в ebp-4 значение регистра eax
009A1080 53 push ebx
009A1081 56 push esi
009A1082 57 push edi //сохраняем в стек значения регистров ebx, esi, edi
18: unsigned char ucA = 4;
009A108D C6 45 FB 04 mov byte ptr [ucA],4 //записываем в локальную переменную ucA значение 4
19: char c = 6;
009A1091 C6 45 FA 06 mov byte ptr [c],6 //записываем в локальную переменную c значение 6
20: int variable = 3;
009A1095 C7 45 F4 03 00 00 00 mov dword ptr [variable],3 //записываем в локальную переменную variable значение 3
21: int* pointer = &variable;
009A109C 8D 45 F4 lea eax,[variable] //загружаем в регистр eax адрес переменной variable
009A109F 89 45 F0 mov dword ptr [pointer],eax //записываем в переменную pointer значение регистра eax
22: Fn(ucA, c, pointer);
009A10A2 8B 45 F0 mov eax,dword ptr [pointer] //записываем в регистр eax переменную pointer
009A10A5 50 push eax //записываем в стек регистр eax
009A10A6 0F B6 4D FA movzx ecx,byte ptr [c] // movzx - Беззнаковое расширение – старшие биты заполняются нулём. Запись в ecx значение C
009A10AA 51 push ecx //записываем в стек регистр ecx
009A10AB 0F B6 55 FB movzx edx,byte ptr [ucA] // movzx - Беззнаковое расширение – старшие биты заполняются нулём. Запись в edx значение ucA
009A10AF 52 push edx //записываем в стек регистр edx
009A10B0 E8 4B FF FF FF call Fn (09A1000h) //call записывает адрес следующей за ней команды в стек и осуществляет переход на первую команду указанной процедуры (Fn)
009A10B5 83 C4 0C add esp,0Ch //в регистр esp записывается сумма esp и 0С(192)
23: return 0;
009A10B8 33 C0 xor eax,eax // обнулениe регистра eax
24: }
009A10BA 5F pop edi
009A10BB 5E pop esi
009A10BC 5B pop ebx //получаем данные (edi, esi, ebx) из стека
009A10BD 8B 4D FC mov ecx,dword ptr [ebp-4] //записываем в регистр ecx значение ebp-4
009A10C0 33 CD xor ecx,ebp // обнулениe регистра ecx
009A10C7 8B E5 mov esp,ebp //записываем в регистр esp значение регистра ebp
009A10C9 5D pop ebp //получаем данные (ebp) из стека
009A10CA C3 ret //завершение выполнение программы
Вывод
В результате выполнения работы была написана программа с вызовом функции на языке C, дизассемблирован исходный код и проанализировано, как компилятор реализует команды языка высокого уровня на ассемблере и обращается к подпрограммам.