КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Регистры состояния и управления
Сегментные регистры Эту группу регистров можно отнести к регистрам состояния. Сегментные регистры (CS, SS, DS, ES, FS и GS) только 16-разрядные. Регистры из этой группы используются при вычислении реального адреса (адреса, который будет передан на шину адреса). Процесс вычисления реального адреса зависит от режима процессора (реальный или защищенный) и будет рассмотрен позже. Названия этих регистров соответствуют выполняемым функциям: – CS (Code Segment, сегмент кода) вместе с EIP (IP) определяют адрес памяти, откуда нужно прочитать следующую инструкцию; – аналогично регистр SS (Stack Segment, сегмент стека) в паре с ESP (SS:SP) указывают на вершину стека. Сегментные регистры DS, ES, FS и GS (Data, Extra, F и G сегменты) используются для адресации данных в памяти. Регистр ESP (SP) – это указатель памяти, который указывает на вершину стека (х86-совместимые процессоры не имеют аппаратного стека). Организацию и назначение стековой памяти мы рассмотрим позже. Также программно не может быть изменен регистр EIP (IP, Instruction Pointer) – указатель команд. Этот регистр указывает на инструкцию, которая будет выполнена следующей. Значение этого регистра изменяется непосредственно контроллером процессора согласно инструкциям, полученным из памяти. Нам осталось рассмотреть только регистр флагов (иногда его называют регистром признаков) – EFLAGS. Он состоит из одноразрядных флагов, отображающих в основном текущее состояние арифметико-логического устройства. Рассмотрим только самые важные из них: • признак нуля ZF (Zero Flag) – 1, если результат предыдущей операции равен нулю; • признак знака SF (Sign Flag) – 1, если результат предыдущей операции отрицательный. • признак переполнения OF (Overflow Flag) – 1, если при выполнении предыдущей операции произошло переполнение (overflow), то есть результат операции больше, чем зарезервированная для него память; • признак переноса CF (Carry Flag) – 1, если бит был «перенесен» и стал битом более высокого порядка; • признак прерывания IF (Interrupt Flag) – 1, если прерывания процессора разрешены; • признак направления DF (Direction Flag) – используется для обработки строк.
Часть 2. Основы программирования на языке Ассемблера процессоров семейства х86 Лекция 6. Структура программы на языке Ассемблера Рассмотрим вопросы организации и компоновки программного кода на языке ассемблера, вопросы взаимодействия различных частей ассемблерной программы, организации сегментов программного кода, данных и стека в контексте различных моделей памяти. 6.1. Организация сегментов Для хорошего понимания, как работает программа на ассемблере, нужно очень четко представлять себе организацию сегментов. Применительно к процессорам Intel Pentium термин «сегмент» имеет два значения: – область физической памяти заранее определенного размера. Для 16-разрядных процессоров размер сегмента физической памяти не может превышать 64 Кбайт, в то время как для 32-разрядных может достигать 4 Гбайт; – область памяти переменного размера, в которой могут находиться программный код, данные или стек. Физический сегмент может располагаться только по адресу, кратному 16, или, как иногда говорят, по границе параграфа. Логические сегменты тесно связаны с физическими. Каждый логический сегмент ассемблерной программы определяет именованную область памяти, которая адресуется селектором сегмента, содержащимся в сегментном регистре. Сегментированная архитектура создает определенные трудности в процессе разработки программ. Для небольших программ, меньших 64 Кбайт, программный код и данные могут размещаться в отдельных сегментах, поэтому никаких особых проблем не возникает. Для больших программ, занимающих несколько сегментов кода или данных, необходимо правильно адресовать данные, находящиеся в разных сегментах данных. Кроме того, если программный код находится в нескольких сегментах, то усложняются реализация переходов и ветвлений в программе, а также вызовы процедур. Во всех этих случаях требуется задавать адреса в виде сегмент:смещение. При использовании 32-разрядного защищенного режима эти проблемы исчезают. Например, в плоской модели памяти (о ней мы поговорим чуть позже) для адресации программного кода и данных достаточно 32-разрядного эффективного адреса внутри непрерывной области памяти. Логические сегменты могут содержать три основных компонента программы: программный код, данные и стек. Макроассемблер MASM обеспечивает правильное отображение этих компонентов на физические сегменты памяти, при этом сегментные регистры CS, DS и SS содержат адреса физических сегментов памяти. 6.2. Директивы управления сегментами и моделями памяти макроассемблера MASM В макроассемблер MASM включены директивы, упрощающие определение сегментов программы и, кроме того, предполагающие те же соглашения, которые используются в языках высокого уровня Microsoft. Упрощенные директивы определения сегментов генерируют необходимый код, указывая при этом атрибуты сегментов и порядок их расположения в памяти. Везде мы будем использовать именно упрощенные директивы определения сегментов, наиболее важные из которых перечислены ниже: • .DATA (.data) – определяет начало сегмента инициализированных данных с именем DATA и при наличии предыдущего сегмента завершает его. Этой директиве должна предшествовать директива.MODEL. Сегмент, определенный с атрибутом.DATA, должен содержать только инициализированные данные, то есть имеющие начальные значения, например: .data val1 dw 11 string1 db "Text string" byte1 db? • .DATA? (.data?) – определяет сегмент данных, в котором располагаются неинициализированные данные. При наличии предыдущего сегмента новый сегмент завершает его. Неинициализированные данные могут объявляться в сегменте.DATA? при помощи оператора?. Преимуществом директивы.DATA? является то, что при ее использовании уменьшается размер исполняемого файла и, кроме того, обеспечивается лучшая совместимость с другими языками. Этой директиве должна предшествовать директива.MODEL. Вот пример использования директивы.DATA?: .data? DB 5 DUP (?) • .CONST (.const) – определяет начало сегмента данных, в котором определены константы. При наличии предыдущего сегмента новый сегмент завершает его. В целях совместимости с другими языками данные должны быть в формате, совместимом с принятыми в языках высокого уровня соглашениями. Сегмент, определенный директивой.CONST, имеет атрибут «только для чтения». Этой директиве должна предшествовать директива.MODEL. • .STACK (.stack) [размер] – определяет начало сегмента стека с указанным размером памяти, который должен быть выделен под область стека. Если параметр не указан, размер стека предполагается равным 1 Кбайт. При наличии предыдущего сегмента новый сегмент завершает его. Этой директиве должна предшествовать директива.MODEL. • .CODE (.code) [имя] – определяет сегмент программного кода и заканчивает предыдущий сегмент, если таковой имеется. Необязательный параметр имя замещает имя TEXT, заданное по умолчанию. Если имя не определено, ассемблер создает сегмент с именем TEXT для моделей памяти tiny, small, compact и flat или сегмент с именем имя_модуля_ТЕХТ для моделей памяти medium, large и huge. Этой директиве должна предшествовать директива.MODEL, указывающая модель памяти, используемую программой. • .MODEL (.model) модель_памяти [,соглашение_о_вызовах] [,тип_ОС] [.параметр_стека] – определяет модель памяти, используемую программой. Директива должна находиться перед любой из директив объявления сегментов. Она связывает определенным образом различные сегменты программы, определяемые ее параметрами tiny, small, compact, medium, 1arge, huge или flat. Параметр модель_памяти является обязательным. Если разрабатывается процедура для включения в программу, написанную на языке высокого уровня, то должна быть указана та модель памяти, которая используется компилятором языка высокого уровня. Кроме того, модель памяти должна соответствовать режиму работы (типу) процессора. Это имеет значение для плоской модели памяти, которую можно применять только в режимах.386,.486,.586,.686. Модель памяти определяет, какой тип адресации данных и команд поддерживает программа (near или far). Это имеет смысл для команд перехода, вызовов и возврата из процедур. В табл. 6.1 демонстрируются эти особенности. В зависимости от «расстояния» переходы бывают трех типов: короткие (short), ближние (near) и дальние (far). Тип перехода задается необязательным параметром инструкции jmp. Если тип не задан, по умолчанию используется тип near. Максимальная «длина» короткого перехода (то есть максимальное расстояние между текущим и целевым адресом) ограничена. Второй байт инструкции (операнд) содержит только одно 8-разрядное значение, поэтому целевой адрес может быть в пределах от -128 до 127 байтов. При переходе выполняется знаковое расширение 8-разрядного значения и его добавление к текущему значению Е(IР). «Длина» ближнего перехода (near) зависит только от режима процессора. В реальном режиме меняется только значение IP, поэтому мы можем «путешествовать» только в пределах одного сегмента (то есть в пределах 64 Кб); в защищенном режиме используется EIP, поэтому целевой адрес может быть где угодно в пределах 4 Гб адресного пространства. Переход типа far модифицирует кроме IP еще и сегментный регистр CS, который используется при вычислении фактического адреса памяти. Поэтому команда перехода должна содержать новое значение CS. Таблица 6.1
Все семь моделей памяти поддерживаются всеми компиляторами MASM, начиная с версии 6.1. Модель small поддерживает один сегмент кода и один сегмент данных. Данные и код при использовании этой модели адресуются как near (ближние). Модель large поддерживает несколько сегментов кода и несколько сегментов данных. По умолчанию все ссылки на код и данные считаются дальними (far). Модель medium поддерживает несколько сегментов программного кода и один сегмент данных, при этом все ссылки в сегментах программного кода по умолчанию считаются дальними (far), а ссылки в сегменте данных – ближними (near). Модель compact поддерживает несколько сегментов данных, в которых используется дальняя адресация данных (far), и один сегмент кода с ближней адресацией (near). Модель huge практически эквивалентна модели памяти large. Нужно отметить, что разработчик программ может явно определить тип адресации данных и команд в различных моделях памяти. Например, ссылки на команды внутри одного сегмента кода в модели large можно сделать ближними (near). Проанализируем, в каких случаях лучше всего подходят те или иные модели памяти. Модель tiny работает только в 16-разрядных приложениях MS-DOS. В этой модели все данные и код располагаются в одном физическом сегменте. Размер программного файла в этом случае не превышает 64 Кбайт. С другой стороны, модель flat предполагает несегментированную конфигурацию программы и используется только в 32-разрядных операционных системах. Эта модель подобна модели tiny в том смысле, что данные и код размещены в одном сегменте, только 32-разрядном. Для разработки программы для модели flat перед директивой.model flat следует разместить одну из директив:.386,.486,.586 или.686. Желательно указывать тот тип процессора, который используется в машине, хотя на машинах с Intel Pentium можно указывать директивы.386 и.486. Операционная система автоматически инициализирует сегментные регистры при загрузке программы, поэтому модифицировать их нужно, только если необходимо смешивать в одной программе 16- и 32-разрядный код. Адресация данных и кода является ближней (near), при этом все адреса и указатели являются 32-разрядными. Параметр соглашение_о_вызовах используется для определения способа передачи параметров при вызове процедуры из других языков, в том числе и языков высокого уровня (С++, Pascal). Параметр может принимать следующие значения: С, BASIC, FORTRAN, PASCAL, SYSCALL, STDCALL. При разработке модулей на ассемблере, которые будут применяться в программах, написанных на языках высокого уровня, обращайте внимание на то, какие соглашения о вызовах поддерживает тот или иной язык. Более подробно соглашения о вызовах мы будем рассматривать при анализе интерфейса программ на ассемблере с программами на языках высокого уровня. Параметр тип_ОС равен 0S_D0S, и на данный момент это единственное поддерживаемое значение этого параметра. Наконец, последний параметр параметр_стека устанавливается равным NEARSTACK (регистр SS равен DS, области данных и стека размещаются в одном и том же физическом сегменте) или FARSTACK (регистр SS не равен DS, области данных и стека размещаются в разных физических сегментах). По умолчанию принимается значение NEARSTACK. Рассмотрим примеры использования директивы. MODEL: .model flat, с Здесь параметр f1at указывает компилятору на то, что будет использоваться 32-разрядная линейная адресация. Второй параметр с указывает, что при вызове ассемблерной процедуры из другой программы (возможно, написанной на другом языке) будет задействован способ передачи параметров, принятый в языке С. Следующий пример: .model large, с, farstack Здесь используются модель памяти large, соглашение о передаче параметров языка С и отдельный сегмент стека (регистр SS не равен DS). .model medium, pascal В этом примере используются модель medium, соглашение о передаче параметров для Pascal и область стека, размещенная в одном физическом сегменте с данными. 6.3. Структура программ на ассемблере MASM Программа, написанная на ассемблере MASM, может состоять из нескольких частей, называемых модулями, в каждом из которых могут быть определены один или несколько сегментов данных, стека и кода. Любая законченная программа на ассемблере должна включать один главный, или основной (main), модуль, с которого начинается ее выполнение. Основной модуль может содержать программные сегменты, сегменты данных и стека, объявленные при помощи упрощенных директив. Кроме того, перед объявлением сегментов нужно указать модель памяти при помощи директивы. MODEL. Поскольку подавляющее большинство современных приложений являются 32-разрядными, то основное внимание необходимо уделить именно таким программам, хотя нужно вспомнить и 16-разрядные программы, которые все еще используются. В следующем примере показана 16-разрядная программа на ассемблере, в которой используются упрощенные директивы ассемблера MASM: .model small, с; эта директива указывается до ; объявления сегментов .stack 100h; размер стека 256 байт
.data; начало сегмента данных
: данные
.code main:; здесь начинается сегмент программ
команды ассемблера
end main end Здесь оператор end main указывает на точку входа main в главную процедуру. Оператор end закрывает последний сегмент и обозначает конец исходного текста программы. В 16-разрядных приложениях MS-DOS можно инициализировать сегментные регистры так, чтобы они указывали на требуемый логический сегмент данных. Следующий пример демонстрирует это: Листинг 6.1. Пример адресации сегментов в программе MS-DOS .model large .data S1 DB "TEST STRINGS" .code mov AX, @data mov DS, AX lea DX, S1 mov AH, 9h int 21h mov ax, 4c00h int 21h end Здесь на экран дисплея выводится строка s1. При помощи следующих команд в сегментный регистр DS помещается адрес сегмента данных, указанного директивой.data: mov АХ, @data mov DS, AX Затем строка s1, адресуемая через регистры DS:DX, выводится на экран с использованием прерывания 9h функции 21h MS-DOS. Попробуйте закомментировать проанализированные две строки кода и посмотреть на результат работы программы. Для 32-разрядных приложений шаблон исходного текста выглядит иначе: .model flat .stack .data : данные .code main: : команды ассемблера end main end Основное отличие от предыдущего примера – другая модель памяти (flat), предполагающая 32-разрядную линейную адресацию с атрибутом near. Как видно из примера, «классический» шаблон 32-разрядного приложения содержит область данных (определяемую директивой.data), область стека (директива.stack) и область программного кода (директива.code).
Лекция 7. Команды и операнды в языке Ассемблера 7.1. Структура команды языка Ассемблера Давайте рассмотрим команду «загрузить число 0x1234 в регистр АХ». На языке ассемблера она записывается очень просто: MOV АХ,0x1234 Для процессора каждая команда представляется в виде двоичного числа (пункт 7 концепции фон Неймана). Ее числовое представление называется машинным кодом. Команда MOV АХ, 0x1234 на машинном языке может быть записана так: 0x1111: 0хВ8, 0x34, 0x12 0x1114: следующие команды В рассматриваемом примере команда помещена по адресу 0x1111. Следующая команда начинается тремя байтами дальше, значит, под команду MOV с операндами отведено 3 байта. Второй и третий байты содержат операнды команды MOV. А что такое 0хВ8? После преобразования 0хВ8 в двоичную систему мы получим значение 10111000b. Первая часть – 1011 – и есть код команды MOV. Встретив код 1011, контроллер «понимает», что перед ним – именно MOV. Следующий разряд (1) означает, что операнды будут 16-разрядными. Три последние цифры определяют регистр назначения. Три нуля соответствуют регистру АХ (или AL, если предыдущий бит был равен 0, указывая таким образом, что операнды будут 8-разрядными). К счастью, нам не придется записывать команды в машинном коде, поскольку ассемблер разрешает использовать их символические имена. Общий формат команды языка ассемблера такой: имя_команды [подсказка] операнды Необязательная подсказка указывает компилятору требуемый размер операнда. Ее значением может быть слово BYTE (8-битный операнд), WORD (16-битный) или DWORD (32-битный). Представим инициализацию некоторой «переменной» нулем, то есть запись нулей по адресу переменной. Подсказка сообщит компилятору размер операнда, то есть сколько именно нулевых байтов должно быть записано по этому адресу. Пока мы не изучили команду для записи значения, поэтому будем считать, что записать значение можно так: mov dword [0x12345678],0; записывает 4 нулевых байта, ; начиная с адреса 0x12 345678 mov word [0x12345678],0; записывает 2 нулевых байта, ; начиная с адреса 0x12345678 mov byte [0x12345678],0; записывает 1 нулевой байт ; по адресу 0x12345 678 7.2. Операнды команд языка Ассемблера Данные, которые обрабатываются командами, называются операндами. Операнды в языке ассемблера записываются непосредственно после команды, если их несколько, то через запятую. Одни команды вообще не имеют никаких операндов, другие имеют один или два операнда. В качестве операнда можно указать непосредственное значение (например, 0x123), имя регистра или ссылку на ячейку памяти (так называемые косвенные операнды). Что же касается разрядности, имеются 32-разрядные, 16-разрядные, и 8-разрядные операнды. Почти каждая команда требует, чтобы операнды были одинакового размера (разрядности). Команда MOV АХ,0x1234 имеет два операнда: операнд регистра и непосредственное значение, и оба они 16-битные. Последний тип операнда – косвенный тип – адресует данные, находящиеся в памяти, получает их из памяти и использует в качестве значения. Узнать этот операнд очень просто – по наличию в записи квадратных скобок. В документации по Ассемблеру различные форматы операндов представлены следующими аббревиатурами: • reg8 -операнд – любой 8-разрядный регистр общего назначения; • regl6 -onepaнд – любой 16-разрядный регистр общего назначения; • reg32 -onepaнд – любой 32-разрядный регистр общего назначения; • m – операнд может находиться в памяти; • imm8 – непосредственное 8-разрядное значение; • imml6 – непосредственное 16-разрядное значение; • imm32 – непосредственное 32-разрядное значение; • segreg – операнд должен быть сегментным регистром. Допускаются неоднозначные типы операндов, например: reg8 / imm8 -onepaнд может быть любым 8-битным регистром общего назначения или любым 8-битным непосредственным значением. Иногда размер операнда определяется только по последнему типу, например, следующая запись аналогична предыдущей: R / imm8 -onepaнд может быть любым регистром (имеется в виду 8-битный регистр) общего назначения или 8-разрядным значением. 7.3. Способы адресации памяти языка Ассемблера Адрес, как и сама команда, – это число. Чтобы не запоминать адреса всех «переменных», используемых в программе, этим адресам присваивают символические обозначения, которые называются переменными (иногда их также называют указателями). В языке Ассемблера используются непосредственная, прямая и косвенная адресации. При использовании косвенного операнда адрес в памяти, по которому находится нужное значение, записывается в квадратных скобках: [адрес]. Если мы используем указатель, то есть символическое представление адреса, например, [ESI], то в листинге машинного кода мы увидим, что указатель был заменен реальным значением адреса. Можно также указать точный адрес памяти, например, [0x594F]. Чаще всего мы будем адресовать память по значению адреса, занесенному в регистр процессора. Чтобы записать такой косвенный операнд, нужно просто написать имя регистра в квадратных скобках. Например, если адрес загружен в регистр ESI, вы можете получить данные, расположенные по этому адресу, используя выражение [ESI]. Рассмотрим ситуацию, когда регистр ESI содержит адрес первого элемента (нумерация начинается с 0) в массиве байтов. Как получить доступ, например, ко второму элементу (элементу, адрес которого на 1 байт больше) массива? Процессор поддерживает сложные способы адресации, которые очень нам пригодятся в дальнейшем. В нашем случае, чтобы получить доступ ко второму элементу массива, нужно записать косвенный операнд [ESI + 1]. Имеются даже более сложные типы адресации: [адрес + ЕВХ + 4]. В этом случае процессор складывает адрес, значение 4 и значение, содержащееся в регистре ЕВХ. Результат этого выражения называется эффективным адресом (ЕА, Effective Address) и используется в качестве адреса, по которому фактически находится операнд. При вычислении эффективного адреса процессор 80386+ также позволяет умножать один член выражения на константу, являющуюся степенью двойки: [адрес + ЕВХ * 4]. Корректным считается даже следующее «сумасшедшее» выражение: [число - 6 + ЕВХ * 8 + ESI]. На практике чаще всего довольствуются только одним регистром [ESI] или суммой регистра и константы, например, [ESI + 4]. В зависимости от режима процессора, мы можем использовать любой 16-разрядный или 32-разрядный регистр общего назначения [ЕАХ], [ЕВХ],... [ЕВР]. Кроме того, в адресации памяти участвуют сегментные регистры. Их функция зависит от режима процессора. Каждый способ адресации предполагает, что при вычислении реального (фактического) адреса используется сегментный регистр по умолчанию. 7.4. Псевдокоманды языка Ассемблера Некоторые из команд могут работать с операндом, расположенным в памяти. Классический пример – команда MOV AX, [number], загружающая в регистр АХ значение из области памяти, адрес которой представлен символическим обозначением «number». Но пока не ясно, как связать символическое обозначение и реальный адрес в памяти. Как раз для этого и служат псевдокоманды. Предложения языка ассемблера делятся на команды и псевдокоманды (директивы). Команды ассемблера – это символические имена машинных команд, обработка их компилятором приводит к генерации машинного кода. Псевдокоманды же управляют работой самого компилятора. На одной и той же аппаратной архитектуре могут работать различные ассемблеры: их команды обязательно будут одинаковыми, но псевдокоманды могут быть разными. Псевдокоманды DB, DW и DD Чаще всего используется псевдокоманда DB (define byte), позволяющая определить числовые константы и строки. Рассмотрим несколько примеров: db 0x55; один байт в шестнадцатеричном виде db 0x55,0x56,0x57; три последовательных байта: 0x55, 0х56, 0x57 db 'а',0x55; можно записать символ в одинарных кавычках ; получится последовательность 0x61, 0x55 db 'Hello',13,10,'$'; можно записать целую строку ; получится 0x48, 0x65, 0х6С, 0х6С, ; 0x6F, 0xD, 0xA, 0x24 Для определения порции данных размера, кратного слову, служит директива DW (define word): dw 0x1234; 0х34, 0x12 dw 'a'; 0x61, 0x00: второй байт заполняется нулями Директива DD (define double word) задает значение порции данных размера, кратного двойному слову: dd 0x12345678; 0х78 0x56 0x34 0x12 dd 1.234567e20; так определяются числа с плавающей точкой А вот так определяется переменная, тот самый «number»: number dd 0x1; переменная number инициализирована ; значением 1 Переменная «number» теперь представляет адрес в памяти, по которому записано значение 0x00000001 длиной в двойное слово. Псевдокоманда EQU Эта директива определяет константу, известную во время компиляции. В качестве значения константы можно указывать также константное выражение. Директиве EQU должно предшествовать символическое имя: four EQU 4; тривиальный пример Псевдокоманды RESB, RESW и RESD Эта группа псевдокоманд позволяет определить неинициализированные данные. Если в первом случае мы заносим данные в память, то сейчас мы просто укажем, сколько байтов нужно зарезервировать для будущих данных. Память для этих неинициализированных переменных распределяется динамически, а сами данные не содержатся в исполнимом файле. Грубо говоря, предыдущую группу инструкций удобно использовать для определения констант, а эту группу – для переменных. Для резервирования памяти служат три директивы: RESB (резервирует байт), RESW (резервирует слово) и RESD (резервирует двойное слово). Аргументом этих псевдокоманд является количество резервируемых позиций: resb 1; резервирует 1 байт resb 2; резервирует 2 байта resw 2; резервирует 4 байта (2 слова) resd 1; резервирует 4 байта number resd 1; резервирует 4 байта для переменной "number" buffer resb 64; резервирует 64 байта для переменной "buffer" Псевдокоманда TIMES Директива TIMES – это псевдокоманда префиксного типа, то есть она используется только в паре с другой командой. Она повторяет последующую псевдокоманду указанное количество раз, подобно директиве DUP из ассемблера Borland TASM. Применяется эта директива в тех случаях, когда нужно «забить» некоторую область памяти повторяющимся образцом. Следующий код определяет строку, состоящую из 64 повторяющихся «Hello»: many_hello: times 64 db 'Hello' Первый аргумент, указывающий количество повторений, может быть и выражением. Например, задачу «разместить строку в области памяти размером в 32 байта и заполнить пробелами оставшееся место» легко решить с помощью директивы TIMES: buffer db "Hello"; определяем строку times 32-($-buffer) db ' '; определяем нужное кол-тво пробелов Выражение 32-($-buffer) возвратит значение 27, потому что $-buffer равно текущей позиции минус позиция начала строки, то есть 5. Вместе с TIMES можно использовать не только псевдокоманды, но и команды процессора: times 5 inc eax; 5 раз выполнить INC EAX
Дата добавления: 2014-12-07; Просмотров: 1008; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |