Семинары по курсу «Архитектура ЭВМ и язык ассемблера» учебно-методическое пособие. Часть 1. - Е.А. Кузьменкова_ В.С. Махнычев_ В.А. Падарян (1110587), страница 6
Текст из файла (страница 6)
она не формирует флаг CF и, следовательно, непозволяет учитывать возможный перенос.ЗадачиЗадача 2-1 Присваивание различных типовРеализовать присваивание b = a; (не более двух команд) при условии:// astatic unsigned char a;static unsigned int b;// bstatic char a;static short b;Задача 2-2 Интерпретация арифметических инструкцийВыпишите значение регистра AL в виде десятичного числа (знакового ибеззнакового), а также флаги CF, OF, ZF и SF после выполнения следующихинструкций.; a; cMOV AL, 199ADD AL, -61MOV AL, -13ADD AL, 179MOV AL, -35SUB AL, 216; dMOV AL, 2SUB AL, 200; b33Задача 2-3 Реализация вычисления арифметических выраженийПриведите фрагмент программы на ассемблере для вычисления следующихвыражений.// astatic int x, y;y = (x / y) * (x % y);// bstatic unsigned char a;static int b;b = (a - 500000) % 10;Задача 2-4 Цифры и числоПустьstatic unsigned short n; // 100 <= n <= 999Приведите фрагмент ассемблерного кода для записи в n числа, полученноговыписыванием в обратном порядке десятичных цифр исходного числа n.Задача 2-5 64 на 32Даны 64-разрядные переменные x и y.
Реализовать операции.// ax += y;// bx -= y;Задача 2-6 Быстрая арифметикаИспользуя команду LEA, реализуйтеарифметических выражений:static int a, b, c;// ac = 10 * a + b + 14;// bc = 24 * a – 15 + b;Задача 2-7 Ошибки в кодеЗачеркните инструкции, содержащие ошибки.34быстроевычислениеследующихX RESD 1ADC WORD [X], WORD [EAX]MUL AL, AHNEG CFSBB DWORD [X], 100MOV EAX, XDIV 15SUB EAX, WORD [X]MOVSX EBX, BLXCHG WORD[X], 100IDIV BYTE [EAX]MOV AL, -150IMUL EAX, 5353.Указатели и адресная арифметикаВзятие адреса и разыменованиеСтатические переменные располагаются в одной из секций статических данных;если у переменных не происходит инициализация при объявлении, торасположить их допустимо в секции .bss.
В языке ассемблера адрес статическойпеременной – ее символическое имя. Это имя необходимо присвоить переменнойxp.static int *xp;static int x;xp = &x;section .bssxp resd 1x resd 1section .textmovdword [xp], xОтображение оператора разыменования в язык ассемблераРасполагаем все переменные в сегменте статической памяти .bss.static int *xp;static int x, y;x = *xp;*xp = y;section .bssxp resd 1x resd 1y resd 1section .textmov edx, dword [xp]mov eax, dword [edx]mov dword [x], eaxmov eax, dword [y]mov dword [edx], eax36;;;;помещаем в EDX значение переменной xpпомещаем в EAX значение ячейкипамяти, на которую ссылается xpприсваиваем это значение x;;;;;помещаем в EAX значение переменной yв регистре EDX уже находится значениепеременной xp.
Это значениеиспользуется как адрес, по которомубудет записано содержимое EAXПример 3-1 Двукратное разыменованиеДана статическая переменная p:static int **p;Требуется на языке ассемблера написать фрагмент программы, который вычисляетвыражение **p + 1 и печатает его значение на стандартный вывод, используямакрокоманду PRINT_DEC.РешениеЗаданное выражение предполагает двойное разыменование указателя иувеличение полученного значения на единицу.%include 'io.inc'section .textglobal CMAINCMAIN:MOV EAX, DWORD [p]MOVEAX, DWORD [EAX]MOV EAX, DWORD [EAX]INC EAXPRINT_DEC EAXXOR EAX, EAXRET;;;;;;;;;;Записываем в регистр EAX значениепеременной pИспользуем это значения для доступак памяти, теперь в регистре EAXне p, а *pПовторяем – теперь в EAX находится **pУвеличиваем это значение на единицуПечатаемПример 3-2 Разыменование и побочные эффектыДан фрагмент кода на языке Си.static short *px, *py;...*px++ = --*py;Требуется привести эквивалентную программу на языке ассемблера.РешениеВ первую очередь требуется определить, какие данные должны быть определеныв ассемблерной программе.
Во фрагменте Си-кода объявлены две переменныетипа short*. Несмотря на то, что тип short занимает два байта, указатель будет37занимать четыре байта, поскольку адресация в IA-32 32-х разрядная, т.е. под адреспамяти необходимы 4 байта. Выписываем следующие директивы ассемблераNasm:section .bsspx resd 1py resd 1Статические переменные располагаются в секциях статических данных. Посколькунет кода инициализации этих переменных, их допустимо разместить в секциистатических неинициализированных данных .bss, в противном случаепеременные были бы объявлены и проинициализированы в секции .data.Для того чтоб было проще корректно реализовать на ассемблере заданноевыражение, преобразуем его, избавившись от побочных эффектов.
Помимо того,расставим скобки для явного указания порядка выполняемых операций.*px++ = --*py;*(px++) = --(*py);--(*py);*px = *py;px++;*py = *py – 1; // (1)*px = *py;// (2)px = px + 1; // (3)Теперь последовательно переводим каждый оператор в соответствующий код наязыке ассемблера.38section .textmov eax, dword [py] ; (1) записываем в регистр eax значение;переменной pydec word [eax];уменьшаем на единицу 16-разрядную;величину, которую адресует регистр;eax, т.е. переменная pymov cx, word [eax] ; (2) записываем в регистр cx эту измененную;величинуmov eax, dword [px] ;записываем в регистр eax значение;переменной pxmov word [eax], cx ;используя это значение, записываем в;то место памяти, которое адресуется;указателем px, текущее значение *pyadd dword [px], 2; (3) Увеличиваем указатель px.
Согласно;правилам адресной арифметики языка Си;значение указателя будет увеличено на;1 * sizeof(short), т.е на 2Пример 3-3 Восстановление кодаФункция, имеющая следующий прототипvoid decode1(int *xp, int *yp, int *zp)была скомпилирована в ассемблерный код. Тело функции выглядит следующимобразом.movmovmovmovmovmovmovmovmovedi, dword [ebp + 8]edx, dword [ebp + 12]ecx, dword [ebp + 16]ebx, dword [edx]esi, dword [ecx]eax, dword [edi]dword [edx], eaxdword [ecx], ebxdword [edi], esi;;;;;;;;;(1)(2)(3)(4)(5)(6)(7)(8)(9)Параметры xp, yp и zp находятся в памяти по смещениям 8, 12 и 16 относительноадреса, содержащегося в регистре ebp.
Напишите код тела функции decode1 наязыке Си, который был бы эквивалентен представленному ассемблерному коду.РешениеВ первых трех инструкциях выполнилась загрузка значений формальныхпараметров функции на регистры.39mov edi, dword [ebp + 8] ; (1) edi ← xpmov edx, dword [ebp + 12] ; (2) edx ← ypmov ecx, dword [ebp + 16] ; (3) ecx ← zpВ следующих трех инструкциях эти регистры использовались для доступа ксодержимому памяти, т.е.
параметры-указатели разыменовывались, исоответствующие значения из памяти пересылалась в регистры ebx, esi, eax.mov ebx, dword [edx]mov esi, dword [ecx]mov eax, dword [edi]; (4) ebx ← *yp; (5) esi ← *zp; (6) eax ← *xpВ последних трех инструкциях регистры edi, edx, ecx, содержащие адреса, сноваиспользовались для доступа к памяти, но в этот раз происходила запись техзначений, которые были ранее размещены на регистрах ebx, esi, eax.mov dword [edx], eaxmov dword [ecx], ebxmov dword [edi], esi; (7) *yp ← eax ← *xp; (8) *zp ← ebx ← *yp; (9) *xp ← esi ← *zpЕсли ввести три вспомогательные переменныесчитываемых из памяти, получим следующий Си-код.дляхранениязначенийvoid decode1(int *xp, int *yp, int *zp) {int y = *yp;int z = *zp;int x = *xp;*yp = x;*zp = y;*xp = z;}Переменная y соответствует ebx, z – esi, x – eax.Указатели и массивыВ языке Си обращение к указателям и массивам происходит единообразно.
К нимможно применять адресную арифметику и индексное выражение. Однако имеетсясущественное отличие в ассемблерном коде, реализующем текстуальноодинаковые выражения. Отличие обусловлено тем, как выделятся память приобъявлении указателей и массивов.40Пример 3-4 Адресная арифметика и массивыДля приведенного фрагмента Си-кода требуется написать соответствующийфрагмент ассемблерной программы.static short *p;static short a[3];…p[1] = *(а + 2);РешениеВ приведенном фрагменте массив используется как указатель, а указатель – какмассив, с индексным выражением. Тем не менее, ассемблерный код будетотражать особенности фактического выделения памяти.
На рисунке ниже показанораспределение данных в памяти: черные линии показывают группировку байт вбазовые типы, серым цветом показаны выделенные байты. Снизу памяти данныеподписаны в терминах языка Си, сверху – адреса, используемые в ассемблерномкоде.Память выделена для указателя p, но не для тех ячеек, на которые он указывает.Поэтому, что вычислить адрес первого элемента последовательности int-ов, накоторые указывает p, необходимо загрузить адрес (значение переменной p) изпамяти в регистр. В случае с массивом память была выделена для всех элементов,а имя массива интерпретируется как адрес начала выделенной памяти. Поэтомупри извлечении элемента с индексом два имя массива следует сразу жеиспользовать в адресном коде в качестве базы.movdx, word [а + 4]movmoveax, dword [p]word [eax + 2], dx;;;;;;*(а + 2) – то же, что и a[2]a – адрес, начиная с которогов памяти размещены элементы массиваЗначение переменной p – адрес,который указывает на началомассива41Пример 3-5 Массив указателейДля приведенного фрагмента Си-кода требуется написать соответствующийфрагмент ассемблерной программы.static int *p[10];static int x;x = *p[8] + 1;РешениеВ первую очередь определим, как данные были расположены в памяти.Элементами массива p являются указатели на целые числа.