Семинары по курсу «Архитектура ЭВМ и язык ассемблера» учебно-методическое пособие. Часть 1. - Е.А. Кузьменкова_ В.С. Махнычев_ В.А. Падарян (1110587), страница 9
Текст из файла (страница 9)
Ниже показаны два эквивалентных варианта.56static int *p, a, b;if (p)a =} elseb =}{*p;{-*p;mov eax, dword [p]test eax, eaxjz .falsemov eax, dword [eax]mov dword [a], eaxjmp .if_end.false:mov eax, dword [eax]neg eaxmov dword [b], eax.if_end:mov eax, dword [p]test eax, eaxjnz .truemov eax, dword [eax]neg eaxmov dword [b], eaxjmp .if_end.true:mov eax, dword [eax]mov dword [a], eax.if_end:Если переход на метку, расположенную ниже в тексте означает ветвления, топереход на ветку, расположенную выше может привести к коду, который ужевыполнялся, что позволяет организовывать циклы.Ниже приведен фрагмент кода, который подсчитывает число символов введеннойстроки, включая последний символ – перевод строки.xor eax, eax.loop_cycle:inc eaxGET_CHAR dlcmp dl, `\n`jne .loop_cycle;;;;;;Обнулили счетчикСюда будем возвращатьсяУвеличиваем счетчикСчитываем что-то со стандартного входаСравниваем с переводом строкиЕсли не конец строки – возвращаемсяПример 5-2 Ветви прорастаютРеализуйте на языке ассемблера приведенный Си-код.static long *a, b, c;...if (a) {if (b > c) {*a += b;}} else {a = &c;*a -= b;}57РешениеКаждая переменная требует для хранения 4 байта памяти, которая должна бытьвыделена в одной из секций статических данных: .data или .bss.Вычисления должны начаться с проверки первого условия.
Если условие не будетвыполняться – следует передать управление на блок кода else, помеченного .l1.В противном случае, т.е. когда условие выполняется, проверяем второе условие.Если оно не выполняется, следует сразу же выходить из объемлющего оператораif-else. Также и из тела вложенного оператора if следует выполнитьбезусловный переход на эту же метку .l2, чтобы не началось выполнение веткиelse объемлющего оператора if-else.section .bssa resd 1b resd 1c resd 1section .textmov edx, dword [b]; Выделяем необходимую память;;;mov eax, dword [a];test eax, eax;jz.l1;cmp edx, dword [c];jle .l2;add dword [eax], edx ;jmp .l2;.l1:;mov dword [a], c;mov eax, c;sub dword [eax], edx ;.l2:Упреждающе загружаем на регистрпеременную b.
Она потребуется в любомслучае.Загружаем на регистр указатель aЕсли указатель нулевой переходим на ветку elseЕсли b меньше или равно c сразу покидаем оба оператораПрибавляем к *a значение переменной bПереходим на конец if-elseНачало ветки elseПрисваиваем a адрес переменной cЭтот же адрес потребуется на регистреВычитаем из *a значение переменной bПример 5-3 Геометрическая прогрессияНапишите программу, вычисляющую i-ый элемент геометрической прогрессии. Навход программе дают три целых числа: i, b, q. На стандартный выход требуетсянапечатать bi. Нумерация элементов начинается с 0. Для хранения чиселиспользуйте тип int, отслеживать переполнение не требуется.РешениеРазместим входные данные в регистрах.
i будем хранить в ecx. Вычисления i-гоэлемента будет проводиться в цикле, условие окончания которого – нулевой ecx.58Если ecx изначально нулевой, тело цикла не должно выполняться. b будем хранитьв eax. Этот же регистр будет использоваться для сохранения результатавычислений i-го элемента. В случае, когда i = 0, eax уже содержит нужное число.Для хранения q будет использоваться edx.%include ‘io.inc’section .textglobal CMAINCMAIN:GET_DEC 4, ecx;GET_DEC 4, eax;GET_DEC 4, edx;jmp .check_condition.loop_body:imul eax, edx;dec ecx;.check_conditioncmp ecx, 0;jne .loop_bodyPRINT_DEC 4, eax;mov eax, 0retВводим iВводим bВводим qВычисляем следующий элементУменьшаем счетчик не единицуИтерируемся, пока счетчик не обнулилсяПечатаем результатВариант «А»Тело цикла содержит одно умножение с накоплением результата в регистре eax иуменьшение счетчика цикла – регистра ecx – на единицу.В рассмотренном примере для организации цикла были использованы командыусловного и безусловного переходов.
Приведем фрагмент другой реализации тогоже примера, где цикл организован с помощью команды LOOP. Считаем, чтоисходные значения регистров ecx, eax и edx уже заданы.jecx .print.loop_body:imul eax, edxloop .loop_body.print:PRINT_DEC 4, eax; Не выполняем тело цикла при ecx = 0Вариант «Б»Тело цикла будет повторено i раз (именно такое значение находится в регистре ecxперед началом цикла), причем команда jecx обеспечит корректное выполнениеданного фрагмента при ecx = 0.59Короткая логикаВыполнение операций короткой логики && и || позволяет сэкономить время засчет того, что если общий результат заранее понятен, исходя из значения левогооперанда – правый операнд не будет вычисляться. Реализация таких операцийдолжна пропускать выполнение части инструкций, что можно обеспечить условнойпередачей управления.Рассмотренный выше пример, в котором указатель проверяется на ноль и толькопотом происходит разыменование, может быть переписан без использованияоператора if.
Реализующий его код не изменится.static int *p, a;mov eax, dword [p]test eax, eaxjz .lmov eax, dword [eax]mov dword [a], eax.l:(p) && (a = *p);Пример 5-4 Восстановление управляющих конструкцийТребуется записать соответствующий данному фрагменту на ассемблере фрагменткода на языке Си.SECTION .textGLOBAL CMAINCMAIN:MOV ESI, DWORD [a]TEST ESI, ESIJE.1MOV ECX, DWORD [b]TEST ECX, ECXJE.1MOV EDX, DWORD [ESI]MOV EAX, EDXSAR EDX, 31IDIV ECXSUB DWORD [ESI], EDX.1:XOR EAX, EAXRET;;;;;;;;;;;(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)(11); (12); (13)РешениеПросматривая ассемблерный код можно заметить обращения к двум статическимпеременным a и b.
В первый момент можно определить только их разрядность –60обе переменные 32-разрядные. Определить, являются они целыми числами илиадресами, можно только просмотрев остальной код. Поэтому временно выпишемобъявление переменных в следующем виде.static int a, b;Далее рассмотрим поток управления: происходит условная передача управленияиз инструкций 3 и 6 на помеченную инструкцию 12.
Причем перед условнойпередачей происходит только загрузка значений переменных на регистры ипроверка этих значений на равенство с нулем (инструкции TEST и JE) . Такимобразом блок инструкций 1-6 может быть представлен в следующем виде:if ((0 != a) && (0 != b)) {...}Вариант «А»if (0 != a) {if (0 != b) {...}}Вариант «Б»Оба варианта допустимы, однако следует отметить необходимость использоватьво варианте «А» именно оператор && короткой логики, поскольку инструкции 4-6выполняются только в том случае, если не сработал условный переход винструкции 3.Теперь рассмотрим блок инструкций 7-11. В первой же инструкции этого блокарегистр ESI, в котором размещено значение переменной a используется вадресном коде, т.е.
переменная a является указателем, а объявление переменныхследует скорректировать.static int *a, b;Значение, взятое из памяти, размещается в паре регистров eax и edx, сразу послечего происходит арифметический сдвиг регистра edx – типовые действия передделением целых чисел, которое, в свою очередь, выполняется в 10 инструкции.Следует отметить, что используется инструкция знакового деления – в объявлениепеременных следует внести модификатор signed, но поскольку он задействованпо умолчанию для типа int, объявление можно не менять.Операндами инструкции idiv выступили величины *a и b, поскольку в моментвыполнения этой инструкции на регистре ecx было расположено именно значениепеременной b. Однако эта инструкция может применяться для реализации двух61операций языка Си – деления и взятия остатка.
Выяснить, какая именно операциябыла реализована можно по тому, какой регистр в дальнейшем использовался ввычислениях. В данном случае это регистр edx (он используется в инструкции 11),что приводит к заключению – реализована операция взятия остатка.Использование этого значения заключается в том, что оно вычитается из числа, накоторое указывает переменная a.
В итоге будет составлено следующее выражениена языке Си.*a = *a – *a % b;Собирая весь код вместе и выполняя некоторое преобразование для выражения,содержащегося в теле оператора if, получаем следующий код.static int *a, b;if ((0 != a) && (0 != b)) {*a -= *a % b;}Условная передача данныхПомимо условной передачи управления архитектура IA-32 позволяетнепосредственно присваивать значения только в случае выполнения заданногоусловия.
Примером такого присвоения является рассмотренная выше инструкцияSETcc. Но ее возможности ограничены – присваивается не произвольное целоечисло, а только true/false.В процессор Pentium Pro была добавлена инструкция CMOVcc, позволяющаяусловно присваивать любое целое число. При выполнении закодированного винструкции условия происходит пересылка в операнд-регистр содержимоговторого операнда, им может быть только регистр или память, непосредственнозакодированная величина недопустима.В приведенном ниже примере порядок выполнения инструкций неизменен, ноприсвоение в eax уменьшенной на 1 величины выполнится только когда а > 0.static int a;(а > 0) && (a--);62movmovdectestcmovgmoveax, dword [a]edx, eaxedxeax, eaxeax, edxdword [a], eaxЗадачиЗадача 5-1 Восстановление типа и операции сравненияДан следующий код на языке Сиint comp(data_t a) {return a COMP 0;}где data_t – некоторый целочисленный тип данных, а COMP – некоторая операциясравнения. Пусть переменная a расположена в регистре EСX.