Семинары по курсу «Архитектура ЭВМ и язык ассемблера» учебно-методическое пособие. Часть 1. - Е.А. Кузьменкова_ В.С. Махнычев_ В.А. Падарян (1110587), страница 3
Текст из файла (страница 3)
Лексема $ соответствует текущейпозиции в транслируемом коде. Выражение $-buffer, записанноенепосредственно после первой строки, будет содержать длину строки ‘hello,world!’.11КонстантыАссемблер NASM поддерживает несколько типов констант: целочисленные,символы, строки и числа с плавающей точкой.У целочисленных констант поддерживаются различные основания: десятичное,двоичное, восьмеричное, шестнадцатеричное. Для явного задания основанияследует воспользоваться соответствующими суффиксами: d, b или y, o или q, h. Поумолчанию последовательность цифр рассматривается как десятичное число.Помимо того, допустимы формы задания основания в виде префиксов: 0d –десятичное 0b – двоичное, 0o – восьмеричное, 0h – шестнадцатеричное.Допускается запись шестнадцатеричных чисел как в Си-программах, с префиксом0x.Целочисленные константы могут содержатьразделения длинных последовательностей цифр.movmovmovmovmovmovmovax,200ax,0200dax,0c8hax,0xc8ax,310qax,11001000bax,1100_1000b;;;;;;;символподчеркиваниядлядесятичноеявно указанное десятичноешестнадцатеричноешестнадцатеричноевосьмеричноедвоичноедвоичноеВо всех случаях приведен один и тот же код.Символьная константа содержит от одного до восьми символов, заключенных впрямые, обратные или двойные кавычки.
Тип кавычек для NASM несущественен,поэтому если используются одинарные кавычки, двойные могут выступать в ролисимвола и, соответственно, наоборот. Обратные кавычки позволяют использоватьспециальные символы языка Си.Символьная константа, состоящая из одного символа, эквивалентна целому числу,равному коду этого символа. Символьная константа, содержащая более одногосимвола, будет транслирована посимвольно в обратном порядке следованиябайтов: 'abcd' эквивалентно не 0x61626364, а 0x64636261. Эта особенностьобусловлена порядком хранения байтов целого числа в памяти: сначала хранятсямладшие байты, за ними — старшие.
Таким образом, если записать эту константу впамять, а затем прочитать побайтово, получится снова abcd, но не dcba.Строковые константы допустимы только в директивах db/dw/dd/dq. От символьныхконстант они отличаются только отсутствием ограничения на длину и12интерпретируются как сцепленные друг с другом символьные константымаксимального допустимого размера.dd 'ninechars';;dd 'nine','char','s' ;db 'ninechars',0,0,0 ;строковая константа – последовательностьдвойных словявно заданы три двойных словапоследовательность байтВо всех случаях определены одни и те же данные.Классы памятиВ стандарте языка Си определены три класса памяти (storage duration):статическая, автоматическая, динамическая.
К статическому классу памятиотносятся глобальные и статические переменные. В зависимости от того, каквыполняетсяинициализация,переменныеэтогоклассапомещаютсякомпилятором либо в секцию .data, либо в секцию .bss. Такие вопросы, какразмещение автоматических локальных переменных и работа с динамическойпамятью, в этой части пособия не рассматриваются.Пример 1-1 Минимальная программаТребуется написать минимальную ассемблерную программу.Решение%include 'io.inc'section .textglobal CMAINCMAIN:MOV EAX, 0RET;;;;;;;(1)(2)(3)(4)(5)(6)(7)Данная ассемблерная программа ничего не делает и при запуске практическисразу возвращает управление операционной системе. Разберём её построчно.Первая строка является директивой ассемблера, она требует включения в текстпрограммы текста файла io.inc, в котором содержатся команды ввода/вывода.Эту строчку следует рассматривать как аналог строки “#include <stdio.h>” в Сипрограммах, однако существует некоторое отличие.
Стандарт языка Си определяетнабор функций, осуществляющих ввод/вывод. Для языка ассемблера такихстандартных функций нет, для облегчения разработки учебных программ былподготовлен файл io.inc, содержащий набор команд ввода/вывода.13Следующая, вторая, строка содержит директиву, указывающую, что последующиестроки относятся к секции кода программы (конкретнее — к секции .text).
Третьястрока оставлена пустой для наглядности отделения директивы от остальноготекста ассемблерной программы.В четвертой строке содержится директива, предписывающая сделать имя CMAINвидимым «снаружи» программы. В данном случае указывается имя CMAIN, котороеопределено в файле io.inc и является точкой входа в программу. На пятой строкеэто имя используется в метке, управление при запуске будет переданопомеченной этим именем инструкции на строке 6. Таким образом, метка CMAINначинает описание функции с соответствующим именем.
Эту функцию следуетсчитать полным аналогом функции main в Си-программах.На шестой строке выполняется инструкция MOV, в которой в регистр EAXпомещается число 0. На следующей строке выполняется инструкция RET,завершающая выполнение функции.Рассмотренный пример можно соотнести со следующей Си-программой.#include <stdio.h>int main () {return 0;}Инструкция 6 в ассемблерной программе соответствует оператору return 0; Онанеобходима для правильной обработки программы в системе автоматическогоприема задач – правильно отработавшая программа должна возвращать число 0.Регистры общего назначенияПроцессор IA-32 содержит восемь 32-разрядных регистров общего назначения:EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP (Рис.
3). Каждый из них допускаетнепосредственный доступ к своей младшей половине по имени соответственно AX,BX, CX, DX, SI, DI, BP, SP. Таким образом, программист имеет возможность работатьс 16-разрядными регистрами с указанными именами. Кроме того, регистры AX, BX,CX, DX в свою очередь допускают независимый доступ к своей младшей и старшейполовине по именам соответственно AL, AH, BL, BH, CL, CH, DL, DH, обеспечиваявозможность работать уже с 8-разрядными регистрами. Буква L в имени регистраобозначает младшую половину соответствующего 16-разрядного регистра, буква H– старшую.14Рисунок 3 – Основные регистры IA-32.Пересылка данныхСамая простая и часто используемая инструкция архитектуры IA-32 — инструкцияпересылки MOV. Она имеет всегда два операнда, которые должны подходить пододин из следующих форматов:MOV регистр, константа — запись значения константы в регистр;MOV регистр-1, регистр-2 — запись значения из регистра-2 в регистр-1;MOV регистр, память — запись в регистр значения из памяти;MOV память, регистр — запись в память значения из регистра;MOV память, константа — запись значения константы в память.Необходимо обратить внимание на то, что целевой операнд находится слева (какбы перед «присваиванием»), а исходный — справа.
Данный порядок операндовявляется одной из характерных черт синтаксиса Intel. Кроме того, важно, что винструкции MOV размеры операндов обязаны совпадать (то есть нельзя переслать врегистр AX из регистра EBX).%include 'io.inc'section .textglobalCMAIN:MOVMOVMOVMOVRETCMAINEAX, 1EBX, EAXCL, 040hEAX, 0;;;;;;;;;;(1)(2)(3)(4)(5)(6) EAX := 1(7) EBX := EAX = 1(8) CL := 040h = 0x40 = 64(9)(10)15Команда XCHG выполняет обмен значений своих операндов (операндыобязательно должны быть одинакового размера). Допустимы следующие форматыэтой команды:XCHG регистр-1, регистр-2XCHG регистр, памятьXCHG память, регистрОбращение к памятиОбращение к операнду в памяти в простейшем случае имеет вид:спецификатор размера [имя переменной], где спецификатор размера(dword, word или byte) задает размер соответствующей переменной в памяти.Например, dword [a] – обращение к переменной a в формате двойного слова (32бита).В записи операнда важную роль играют квадратные скобки вокруг именипеременной.
Именно такая запись трактуется в NASM как содержимое ячейкипамяти по указанному адресу, т.е. значение по данному адресу, тогда как простоимя переменной трактуется как адрес соответствующей ячейки памяти. Сравните:mov eax, dword [a] ; В регистр eax помещается значение переменной amov eax, a; В регистр eax помещается адрес переменной aПри выполнении второй команды обращения к памяти не происходит.Сложение и вычитаниеАрифметические инструкции сложения и вычитания для целых чисел называютсяADD и SUB.
У них нет отдельных версий для знаковых и беззнаковых целых чисел,они применимы в обоих случаях.ADD EBX, EAX; EBX := EBX + EAX = 1 + 1 = 2, EAX не меняетсяADD EBX, EBX; EBX := EBX + EBX = 2 * EBX = 4SUB EAX, 2; EAX := EAX - 2 = 1 - 2 = -1 = 0FFFFFFFFhADD AX, 1; AX := AX + 1 = 0FFFFh + 1 = -1 + 1 = 0; EAX стал равен 0FFFF0000h16Первый операнд ADD и SUB — целевой.
Это тот операнд, к значению которого будетприбавлено (или от которого будет отнято) значение второго операнда. Результатоперации также записывается в первый операнд. Целевой операнд может бытьрегистром или операндом в памяти. Второй операнд (то, что прибавляется илиотнимается) может быть регистром, операндом в памяти или константой.Важно: ни в какой инструкции, кроме строковых, не может быть два операнда впамяти.Пример 1-2 Определение значения регистраПусть ассемблерная переменная A имеет значение 0x CAFE BABE. Требуетсявыписать в шестнадцатеричном виде значение регистра AX после выполненияследующих инструкций.MOV AX, WORD [A + 2]ADD AX, 3РешениеРассмотрим расположение в памяти переменной A и определим, что будет врегистре AX, после выполнения первой инструкции.
Поскольку в архитектуре IA-32используется обратное расположение байтов в памяти, то получаем следующее:Байт с адресом A+2 будет иметь значение 0xFE, следующий за ним – 0xCA. Припересылке в регистр байты поменяются местами, и регистр AX будет иметьзначение 0xCAFE. После того, как к этому значению будет прибавлено 3, оно станетравным 0xCB01.Пример 1-3 Переворот байтов в двойном словеВ памяти последовательно расположены 4 переменных a, b, c и d размером 1 байткаждая в заданном порядке. Требуется сформировать 32-битное число в регистреEAX таким образом, чтобы старший байт числа совпадал со значением переменнойa, следующий за ним байт – со значением b, следующий – со значением c, и,наконец, младший байт – со значением d. Пусть, для примера, значения a, b, c и d17равны 1, 2, 3, 4 соответственно.