Что потребуется для работы с ассемблером — КиберПедия 

Своеобразие русской архитектуры: Основной материал – дерево – быстрота постройки, но недолговечность и необходимость деления...

Адаптации растений и животных к жизни в горах: Большое значение для жизни организмов в горах имеют степень расчленения, крутизна и экспозиционные различия склонов...

Что потребуется для работы с ассемблером

2021-04-18 92
Что потребуется для работы с ассемблером 0.00 из 5.00 0 оценок
Заказать работу

Введение

Первый вопрос, который задает себе человек, впервые услышавший о языке программирования Ассемблер — а зачем он, собственно, нужен? Особенно теперь, когда все пишут на C/C++, Delphi или других языках высокого уровня? Ведь очень многое можно создать на С, но ни один язык, даже такой популярный, не может претендовать на то, чтобы на нем можно было написать действительно «все».

Итак, на ассемблере пишут:

  • все, что требует максимальной скорости выполнения: основные компоненты компьютерных игр, ядра операционных систем реального времени и просто критические участки программ;
  • все, что взаимодействует с внешними устройствами: драйверы, программы, работающие напрямую с портами, звуковыми и видеоплатами;
  • все, что использует полностью возможности процессора: ядра многозадачных операционных систем, DPMI-серверы и вообще любые программы, переводящие процессор в защищенный режим;
  • все, что полностью использует возможности операционной системы: вирусы и антивирусы, защиты от несанкционированного доступа, программы, обходящие эти защиты, и программы, защищающиеся от этих программ;
  • и многое другое.

Стоит познакомиться с ассемблером поближе, как оказывается, что многое из того, что обычно пишут на языках высокого уровня, лучше, проще и быстрее написать на ассемблере.

Как же так? — спросите вы, прочитав последний пункт. — Ведь всем известно, что ассемблер — неудобный язык, и писать на нем долго и сложно! Давайте перечислим мотивы, которые обычно выдвигаются в доказательство того, что ассемблер не нужен.

Говорят, что ассемблер трудно выучить. Любой язык программирования трудно выучить. Легко выучить С или Delphi после Паскаля, потому что они похожи. А попробуйте освоить Lisp, Forth или Prolog, и окажется, что ассемблер в действительности даже проще, чем любой совершенно незнакомый язык программирования.

Говорят, что программы на ассемблере трудно понять. Разумеется, на ассемблере легко написать неудобочитаемую программу... точно так же, как и на любом другом языке! Если вы знаете язык и если автор программы не старался ее запутать, то понять программу будет не сложнее, чем если бы она была написана на Бейсике.

Говорят, что программы на ассемблере трудно отлаживать. Программы на ассемблере легко отлаживать — опять же при условии, что вы знаете язык. Более того, знание ассемблера часто помогает отлаживать программы на других языках, потому что оно дает представление о том, как на самом деле функционирует компьютер и что происходит при выполнении команд языка высокого уровня.

Говорят, что современные компьютеры такие быстрые, что ассемблер больше не нужен. Каким бы быстрым ни был компьютер, пользователю всегда хочется большей скорости, иначе не наблюдалось бы постоянного спроса на еще более быстрые компьютеры. И самой быстрой программой на данном оборудовании всегда будет программа, написанная на ассемблере.

Говорят, что писать на ассемблере сложно. В этом есть доля правды. Очень часто авторы программ на ассемблере «изобретают велосипеды», программируя заново элементарные процедуры типа форматированного вывода на экран или генератора случайных чисел, в то время как программисты на С просто вызывают стандартные функции. Библиотеки таких функций существуют и для ассемблера, но они не стандартизованы и не распространяются вместе с компиляторами.

Говорят, что программы на ассемблере не переносятся. Действительно, в этом заключается самая сильная и самая слабая сторона ассемблера. С одной стороны, благодаря этой особенности программы на ассемблере используют возможности компьютера с наибольшей полнотой с другой стороны, эти же программы не будут работать на другом компьютере. Стоит заметить, что и другие языки часто не гарантируют переносимости — та же программа на С, написанная, например, под Windows 95, не скомпилируется ни на Macintosh, ни на SGI.

Далеко не все, что говорят об ассемблере, является правдой, и далеко не все, кто говорят об ассемблере, на самом деле знают его. Но даже ярые противники согласятся с тем, что программы на ассемблере — самые быстрые, самые маленькие и могут то, что не под силу программам, созданным на любом другом языке программирования.

Это методическое пособие предназначено для  читателей с разным уровнем подготовки — от начинающих, которые хотят познакомиться с ассемблером серьезно или желают лишь написать пару программ, выполняющих необычные трюки с компьютером, до профессиональных программистов, которые тоже могут найти здесь интересные разделы. Почти все, что надо знать об ассемблере, где-нибудь да объяснено, а также объяснено многое из того, что не заботит большинство программистов. С одной стороны, чтобы написать простую программу, не нужно знать язык и устройство процессора в совершенстве, но, с другой стороны, по-настоящему серьезная работа потребует и серьезной подготовки. Уровень сложности в этом пособие возрастает от начала к концу,. Впрочем, если у вас есть время и желание выучить ассемблер с нуля, — читайте все по порядку. Если же вам хочется немедленно приступить к написанию программ, начните сразу «Методического пособия Часть 2», но будьте готовы к тому, что иногда придется возвращаться к предыдущим главам за более подробным описанием тех или иных команд. И наконец, если вам уже доводилось программировать на ассемблере, — выбирайте то, что интересно.

Предварительные сведения

Регистры процессора

Начиная с 80386, процессоры Intel предоставляют 16 основных регистров для пользовательских программ плюс еще 11 регистров для работы с числами с плавающей запятой (FPU/NPX) и мультимедийными приложениями (MMX). Все команды так или иначе изменяют значения регистров, и всегда быстрее и удобнее обращаться к регистру, чем к памяти.

Помимо основных регистров из реального (но не из виртуального) режима доступны также регистры управления памятью (GDTR, IDTR, TR, LDTR) регистры управления (CR0, CR1 – CR4), отладочные регистры (DR0 – DR7) и машинно-специфичные регистры, но они не применяются для повседневных задач и будут рассматриваться в соответствующих главах позже.

Регистры общего назначения

32-битные регистры EAX (аккумулятор), EBX (база), ECX (счетчик), EDX (регистр данных) могут использоваться без ограничений для любых целей — временного хранения данных, аргументов или результатов различных операций. Названия этих регистров происходят от того, что некоторые команды применяют их специальным образом: так, аккумулятор часто используется для хранения результата действий, выполняемых над двумя операндами, регистр данных в этих случаях получает старшую часть результата, если он не умещается в аккумулятор, регистр-счетчик используется как счетчик в циклах и строковых операциях, а регистр-база используется при так называемой адресации по базе. Младшие 16 бит каждого из этих регистров могут использоваться как самостоятельные регистры и имеют имена (соответственно AX, BX, CX, DX). На самом деле в процессорах 8086 – 80286 все регистры имели размер 16 бит и назывались именно так, а 32-битные EAX – EDX появились с введением 32-битной архитектуры в 80386. Кроме этого, отдельные байты в 16-битных регистрах AX – DX тоже имеют свои имена и могут использоваться как 8-битные регистры. Старшие байты этих регистров называются AH, BH, CH, DH, а младшие — AL, BL, CL, DL (рис. 3).

AX

EAX   АН AL

BX

EBX   BL

CX

ECX   CL

DX

EDX   DL

                                                      31           16 15 8 7   0

 

ESI   SI
EDI   DI
EBP   BP
ESP   SP

Рис. 3. Регистры общего назначения

 

Другие четыре регистра общего назначения — ESI (индекс источника), EDI (индекс приемника), EBP (указатель базы), ESP (указатель стека) — имеют более конкретное назначение и могут применяться для хранения всевозможных временных переменных, только когда они не используются по назначению. Регистры ESI и EDI используются в строковых операциях, EBP и ESP используются при работе со стеком (см. параграф 2.1.3). Так же, как и с регистрами EAX – EDX, младшие половины этих четырех регистров называются SI, DI, BP и SP соответственно, и в процессорах до 80386 только они и присутствовали.

Сегментные регистры

При использовании каждой из сегментированных моделей памяти для формирования любого адреса применяются два числа — адрес начала сегмента и смещение искомого байта относительно этого начала (в бессегментной модели памяти flat адреса начал всех сегментов равны). Операционные системы (кроме DOS) могут размещать сегменты, с которыми работает программа пользователя, в разных местах в памяти, и даже могут временно записывать их на диск, если памяти не хватает. Так как сегменты могут оказаться где угодно, программа обращается к ним, используя вместо настоящего адреса начала сегмента 16-битное число, называемое селектором. В процессорах Intel предусмотрено шесть шестнадцатибитных регистров — CS, DS, ES, FS, GS, SS, используемых для хранения селекторов. (Регистры FS и GS отсутствовали в 8086, но появились уже в 80286.) Это не значит, что программа не может одновременно работать с большим количеством сегментов памяти, — в любой момент времени можно изменить значения, записанные в этих регистрах.

Замечание: В реальном режиме селектор любого сегмента равен адресу его начала, деленному на 16. Чтобы получить адрес в памяти, 16-битное смещение складывают с этим селектором, сдвинутым предварительно влево на 4 разряда. Таким образом, оказывается, что максимальный доступный адрес в реальном режиме 220-1 = 1 048 575. Для сравнения, в защищенном режиме адрес начала для каждого сегмента хранится отдельно, так что возможно 246 (64 терабайта) различных логических адресов в формате сегмент:смещение (программа может определить до 16384 сегментов, каждый из которых до 4 Гб), хотя реально процессор может адресоваться только к 4 или 64 (для Pentium Pro) гигабайтам памяти.

В отличие от регистров DS, ES, GS, FS, которые называются регистрами сегментов данных, регистры CS и SS отвечают за сегменты двух особенных типов — сегмент кода и сегмент стека. Сегмент кода содержит программу, исполняющуюся в данный момент, так что запись нового селектора в этот регистр приводит к тому, что далее будет исполнена не следующая по тексту программы команда, а команда из кода, находящегося в другом сегменте, с тем же смещением. Смещение следующей выполняемой команды всегда хранится в специальном регистре — EIP (указатель инструкции, шестнадцатибитная форма IP), запись в который также приведет к тому, что следующей будет исполнена какая-нибудь другая команда. На самом деле все команды передачи управления — перехода, условного перехода, цикла, вызова подпрограммы и т.п. — и осуществляют эту самую запись в CS и EIP.

Стек

Стек — это специальным образом организованный участок памяти, используемый для временного хранения переменных, для передачи параметров вызываемым подпрограммам и для сохранения адреса возврата при вызове процедур и прерываний. Легче всего представить стек в виде стопки листов бумаги (это одно из значений слова «stack» в английском языке) — вы можете класть и забирать листы бумаги только с вершины стопки. Таким образом, если записать в стек числа 1, 2, 3, то при чтении они будут получаться в обратном порядке — 3, 2, 1. Стек располагается в сегменте памяти, описываемом регистром SS, а текущее смещение вершины стека записано в регистре ESP, причем при записи в стек значение этого смещения уменьшается, то есть стек растет вниз от максимально возможного адреса (рис. 4). Такое расположение стека «вверх ногами» может быть необходимо, например в бессегментной модели памяти, когда все сегменты, включая сегмент стека и сегмент кода, занимают одну и ту же область — всю память. Тогда программа исполняется в нижней области памяти, в области малых адресов, и EIP растет, а стек располагается в верхней области памяти, и ESP уменьшается.

 

                                                                                           Дно стека     

FFFFFFFC

                               FFFFFFF8                                       Параметры

                          FFFFFFF4

                          FFFFFFF0                                                EBP=FFFFFFF0              

FFFFFFEC

FFFFFFE8                                       локальные переменные

FFFFFFE4                                           ESP=FFFFFFE4

 

 

Рис. 4. Стек

 

При вызове подпрограммы параметры в большинстве случаев помещают в стек, а в EBP записывают текущее значение ESP. Тогда, если подпрограмма использует стек для хранения локальных переменных, ESP изменится, но EBP можно будет использовать для того, чтобы считывать значения параметров напрямую из стека (их смещения будут записываться как EBP + номер параметра). Более подробно вызовы подпрограмм и все возможные способы передачи параметров рассмотрены в главе 4.3.2.

Регистр флагов

Еще один важный регистр, использующийся при выполнении большинства команд, — регистр флагов EFLAGS. Как и раньше, его младшие 16 бит, представлявшие собой весь этот регистр до 80386, называются FLAGS. В этом регистре каждый бит является флагом, то есть устанавливается в 1 при определенных условиях или установка его в 1 изменяет поведение процессора. Все флаги, расположенные в старшем слове регистра EFLAGS, имеют отношение к управлению защищенным режимом, поэтому здесь рассмотрен только регистр FLAGS (рис. 5).

     

0 NT IOPL OF DF IF TF SF ZF 0 AF 0 PF 1 CF

Рис. 5. Регистр флагов FLAGS

 

  • CF — флаг переноса. Устанавливается в 1, если результат предыдущей операции не уместился в приемнике и произошел перенос из старшего бита или если требуется заем (при вычитании), иначе устанавливается в 0. Например, после сложения слова 0FFFFh и 1, если регистр, в который надо поместить результат, — слово, в него будет записано 0000h и флаг CF = 1.
  • PF — флаг четности. Устанавливается в 1, если младший байт результата предыдущей команды содержит четное число бит, равных 1; устанавливается в 0, если число единичных бит нечетное. (Это не то же самое, что делимость на два. Число делится на два без остатка, если его самый младший бит равен нулю, и не делится, если он равен 1.)
  • AF — флаг полупереноса или вспомогательного переноса. Устанавливается в 1, если в результате предыдущей операции произошел перенос (или заем) из третьего бита в четвертый. Этот флаг используется автоматически командами двоично-десятичной коррекции.
  • ZF — флаг нуля. Устанавливается в 1, если результат предыдущей команды — ноль.
  • SF — флаг знака. Этот флаг всегда равен старшему биту результата.
  • TF — флаг ловушки. Этот флаг был предусмотрен для работы отладчиков, не использующих защищенный режим. Установка его в 1 приводит к тому, что после выполнения каждой команды программы управление временно передается отладчику (вызывается прерывание 1 — см. описание команды INT).
  • IF — флаг прерываний. Установка этого флага в 1 приводит к тому, что процессор перестает обрабатывать прерывания от внешних устройств (см. описание команды INT). Обычно его устанавливают на короткое время для выполнения критических участков кода.
  • DF — флаг направления. Этот флаг контролирует поведение команд обработки строк — когда он установлен в 1, строки обрабатываются в сторону уменьшения адресов, а когда DF = 0 — наоборот.
  • OF — флаг переполнения. Этот флаг устанавливается в 1, если результат предыдущей арифметической операции над числами со знаком выходит за допустимые для них пределы. Например, если при сложении двух положительных чисел получается число со старшим битом, равным единице (то есть отрицательное) и наоборот.

Флаги IOPL (уровень привилегий ввода-вывода) и NT (вложенная задача) применяются в защищенном режиме.

Способы адресации

Большинство команд процессора вызывается с аргументами, которые в ассемблере принято называть операндами. Например: команда сложения содержимого регистра с числом требует задания двух операндов — содержимого регистра и числа. Далее рассмотрены все существующие способы задания адреса хранения операндов — способы адресации.

Регистровая адресация

Операнды могут располагаться в любых регистрах общего назначения и сегментных регистрах. В этом случае в тексте программы указывается название соответствующего регистра, например команда, копирующая в регистр AX содержимое регистра BX, записывается как

mov ax,bx

Непосредственная адресация

Некоторые команды (все арифметические команды, кроме деления) позволяют указывать один из операндов непосредственно в тексте программы, например команда

mov ax,2

помещает в регистр AX число 2.

Прямая адресация

Если известен адрес операнда, располагающегося в памяти, можно использовать этот адрес. Если операнд — слово, находящееся в сегменте, на который указывает ES, со смещением от начала сегмента 0001, то команда

mov ax,es:0001

поместит это слово в регистр AX. В реальных программах обычно для задания статических переменных используют директивы определения данных (глава 3.3), которые позволяют ссылаться на статические переменные не по адресу, а по имени. Тогда, если в сегменте, указанном в ES, была описана переменная word_var размером в слово, можно записать ту же команду как

mov ax,es:word_var

В таком случае ассемблер сам заменит слово «word_var» на соответствующий адрес. Если селектор сегмента данных находится в DS, имя сегментного регистра при прямой адресации можно не указывать, DS используется по умолчанию. Прямая адресация иногда называется адресацией по смещению.

Адресация отличается для реального и защищенного режимов. В реальном режиме (так же как и в режиме V86) смещение всегда 16-битное, это значит, что ни непосредственно указанное смещение, ни результат сложения содержимого разных регистров в более сложных методах адресации не могут превышать границ слова. При программировании для Windows, для DOS4G, PMODE и в других ситуациях, когда программа будет запускаться в защищенном режиме, смещение не может превышать границ двойного слова.

Косвенная адресация

По аналогии с регистровыми и непосредственными операндами адрес операнда в памяти также можно не указывать непосредственно, а хранить в любом регистре. До 80386 для этого можно было использовать только BX, SI, DI и BP, но потом эти ограничения были сняты и адрес операнда разрешили считывать также и из EAX, EBX, ECX, EDX, ESI, EDI, EBP и ESP (но не из AX, CX, DX или SP напрямую — надо использовать EAX, ECX, EDX, ESP соответственно или предварительно скопировать смещение в BX, SI, DI или BP). Например, следующая команда помещает в регистр AX слово из ячейки памяти, селектор сегмента которой находится в DS, а смещение — в BX:

mov ax,[bx]

Как и в случае прямой адресации, DS используется по умолчанию, но не во всех случаях: если смещение берут из регистров ESP, EBP или BP, то в качестве сегментного регистра используется SS. В реальном режиме можно свободно пользоваться всеми 32-битными регистрами, надо только следить, чтобы их содержимое не превышало границ 16-битного слова.

Пересылка данных

· Команда: MOV приемник, источник
· Назначение: Пересылка данных
· Процессор: 8086

Базовая команда пересылки данных. Копирует содержимое источника в приемник, источник не изменяется. Команда MOV действует аналогично операторам присваивания из языков высокого уровня, то есть команда

mov ax,bx

эквивалентна выражению

ах:= bх;

языка Паскаль или

ах = bх;

языка С, за исключением того, что команда ассемблера позволяет работать не только с переменными в памяти, но и со всеми регистрами процессора.

В качестве источника для MOV могут использоваться: число (непосредственный операнд), регистр общего назначения, сегментный регистр или переменная (то есть операнд, находящийся в памяти). В качестве приемника — регистр общего назначения, сегментный регистр (кроме CS) или переменная. Оба операнда должны быть одного и того же размера — байт, слово или двойное слово.

Нельзя выполнять пересылку данных с помощью MOV из одной переменной в другую, из одного сегментного регистра в другой и нельзя помещать в сегментный регистр непосредственный операнд — эти операции выполняют двумя командами MOV (из сегментного регистра в обычный и уже из него в другой сегментный) или парой команд PUSH/POP.

Примечание: Загрузка регистра SS командой MOV автоматически запрещает прерывания до окончания следующей за этим команды MOV, так что можно загрузить SS и ESP двумя последовательными командами MOV, не опасаясь, что в этот момент произойдет прерывание, обработчик которого получит неправильный стек. В любом случае для загрузки значения в регистр SS предпочтительнее команда LSS.

· Команда: CMOVcc приемник, источник
· Назначение: Условная пересылка данных
· Процессор: P6

Это набор команд, которые копируют содержимое источника в приемник, если удовлетворяется то или иное условие (см. табл. 5). Источником может быть регистр общего назначения или переменная, а приемником — только регистр. Условие, которое должно удовлетворяться, — просто равенство нулю или единице тех или иных флагов из регистра FLAGS, но, если использовать команды CMOVcc сразу после команды СМР (сравнение) с теми же операндами, условия приобретают особый смысл, например:

cmp ах,bх; сравнить ах и bх

cmovl ax,bx; если ах < bх, скопировать bх в ах

Слова «выше» и «ниже» в таблице 5 относятся к сравнению чисел без знака, слова «больше» и «меньше» учитывают знак.

 

Таблица 5. Разновидности команды CMOVcc

 

Код команды Реальное условие Условие для CMP
CMOVA CMOVNBE CF = 0 и ZF = 0 если выше если не ниже или равно
CMOVAE CMOVNB CMOVNC CF = 0 если выше или равно если не ниже если нет переноса
CMOVB CMOVNAE CMOVC CF = 1 если ниже если не выше или равно если перенос
CMOVBE CMOVNA CF = 1 и ZF = 1 если ниже или равно если не выше
CMOVE CMOVZ ZF = 1 если равно если ноль
CMOVG CMOVNLE ZF = 0 и SF = OF если больше если не меньше или равно
CMOVGE CMOVNL SF = OF если больше или равно если не меньше
CMOVL CMOVNGE SF <> OF если меньше если не больше или равно
CMOVLE CMOVNG ZF = 1 и SF <> OF если меньше или равно если не больше
CMOVNE CMOVNZ ZF = 0 если не равно если не ноль
CMOVNO OF = 0 если нет переполнения
CMOVO OF = 1 если есть переполнение
CMOVNP CMOVPO PF = 0 если нет четности если нечетное
CMOVP CMOVPE PF = 1 если есть четность если четное
CMOVNS SF = 0 если нет знака
CMOVS SF = 1 если есть знак

 

· Команда: XCHG операнд1, операнд2
· Назначение: Обмен операндов между собой
· Процессор: 8086

Содержимое операнда 2 копируется в операнд 1, а старое содержимое операнда 1 — в операнд 2. XCHG можно выполнять над двумя регистрами или над регистром и переменной.

xchg eax,ebx; то же, что три команды на языке С:

                  ; temp = eax; eax = ebx; ebx = temp;

xchg al,al; а эта команда не делает ничего

· Команда: BSWAP регистр32
· Назначение: Обмен байт внутри регистра
· Процессор: 80486

Обращает порядок байт в 32-битном регистре. Биты 0 – 7 (младший байт младшего слова) меняются местами с битами 24 – 31 (старший байт старшего слова), а биты 8 – 15 (старший байт младшего слова) меняются местами с битами 16 – 23 (младший байт старшего слова).

mov eax,12345678h

bswap eax ; теперь в еах находится 78563412h

Чтобы обратить порядок байт в 16-битном регистре, следует использовать команду XCHG:

  xchg al,ah; обратить порядок байт в АХ

В процессорах Intel команду BSWAP можно использовать и для обращения порядка байт в 16-битных регистрах, но в некоторых совместимых процессорах других фирм этот вариант BSWAP не реализован.

· Команда: PUSH источник
· Назначение: Поместить данные в стек
· Процессор: 8086

Помещает содержимое источника в стек. Источником может быть регистр, сегментный регистр, непосредственный операнд или переменная. Фактически эта команда копирует содержимое источника в память по адресу SS:[ESP] и уменьшает ESP на размер источника в байтах (2 или 4). Команда PUSH практически всегда используется в паре с POP (считать данные из стека). Так, например, чтобы скопировать содержимое одного сегментного регистра в другой (что нельзя выполнить одной командой MOV), можно использовать такую последовательность команд:

push cs

pop ds; теперь DS указывает на тот же сегмент, что и CS

Другое частое применение команд PUSH/POP — временное хранение переменных, например:

push eax; сохраняет текущее значение ЕАХ

...      ; здесь располагаются какие-нибудь команды,

             ; которые используют ЕАХ, например CMPXCHG

pop eax; восстанавливает старое значение ЕАХ

Начиная с 80286, команда PUSH ESP (или SP) помещает в стек значение ESP до того, как эта же команда его уменьшит, в то время как на 8086 SP помещался в стек уже уменьшенным на два.

· Команда: POP приемник
· Назначение: Считать данные из стека
· Процессор: 8086

Помещает в приемник слово или двойное слово, находящееся в вершине стека, увеличивая ESP на 2 или 4 соответственно. POP выполняет действие, полностью обратное PUSH. Приемником может быть регистр общего назначения, сегментный регистр, кроме CS (чтобы загрузить CS из стека, надо воспользоваться командой RET), или переменная. Если в роли приемника выступает операнд, использующий ESP для косвенной адресации, команда POP вычисляет адрес операнда уже после того, как она увеличивает ESP.

· Команда: PUSHA PUSHAD
· Назначение: Поместить в стек все регистры общего назначения
· Процессор: 80186 80386

PUSHA помещает в стек регистры в следующем порядке: АХ, СХ, DX, ВХ, SP, ВР, SI и DI. PUSHAD помещает в стек ЕАХ, ЕСХ, EDX, ЕВХ, ESP, EBP, ESI и EDI. (В случае SP и ESP используется значение, которое находилось в этом регистре до начала работы команды.) В паре с командами POPA/POPAD, считывающими эти же регистры из стека в обратном порядке, это позволяет писать подпрограммы (обычно обработчики прерываний), которые не должны изменять значения регистров по окончании своей работы. В начале такой подпрограммы вызывают команду PUSHA, а в конце — РОРА.

Примечание: На самом деле PUSHA и PUSHAD — одна и та же команда с кодом 60h. Ее поведение определяется тем, выполняется ли она в 16- или в 32-битном режиме. Если программист использует команду PUSHAD в 16-битном сегменте или PUSHA в 32-битном, ассемблер просто записывает перед ней префикс изменения размерности операнда (66h).

Это же будет распространяться на некоторые другие пары команд: РОРА/POPAD, POPF/POPFD, PUSHF/PUSHFD, JCXZ/JECXZ, CMPSW/CMPSD, INSW/INSD, LODSW/LODSD, MOVSW/MOVSD, OUTSW/OUTSD, SCASW/SCASD и STOSW/STOSD.

· Команда: POPA POPAD
· Назначение: Загрузить из стека все регистры общего назначения
· Процессор: 80186 80386

Эти команды выполняют действия, полностью обратные действиям PUSHA и PUSHAD, за исключением того, что помещенное в стек значение SP или ESP игнорируется. РОРА загружает из стека DI, SI, BP, увеличивает SP на два, загружает ВХ, DX, CX, AX, a POPAD загружает EDI, ESI, ЕВР, увеличивает ESP на 4 и загружает ЕВХ, EDX, ЕСХ, ЕАХ.

· Команда: IN приемник, источник
· Назначение: Считать данные из порта
· Процессор: 8086

Копирует число из порта ввода-вывода, номер которого указан в источнике, в приемник. Приемником может быть только AL, АХ или ЕАХ. Источник — или непосредственный операнд, или DX, причем можно указывать только номера портов не больше 255.

· Команда: OUT приемник, источник
· Назначение: Записать данные в порт
· Процессор: 8086

Копирует число из источника (AL, АХ или ЕАХ) в порт ввода-вывода, номер которого указан в приемнике. Приемник может быть либо непосредственным номером порта, либо регистром DX. На командах IN и OUT строится все общение процессора с устройствами ввода-вывода — клавиатурой, жесткими дисками, различными контроллерами, и используются они, в первую очередь, в драйверах устройств. Например, чтобы включить динамик PC, достаточно выполнить команды:

in  al,61h

or  al,3

out 61h,al

Программирование портов ввода-вывода рассмотрено подробно в главе 5.10.

· Команда: CWD
· Назначение: Конвертирование слова в двойное слово
· Процессор: 8086
· Команда: CDQ
· Назначение: Конвертирование двойного слова в учетверенное
· Процессор: 80386

Команда CWD превращает слово в AХ в двойное слово, младшая половина которого (биты 0 – 15) остается в АХ, а старшая (биты 16 – 31) располагается в DX. Команда CDQ выполняет аналогичное действие по отношению к двойному слову в ЕАХ, расширяя его до учетверенного слова в EDX:EAX. Эти команды всего лишь устанавливают все биты регистра DX или EDX в значение, равное значению старшего бита регистра АХ или ЕАХ, сохраняя таким образом его знак.

· Команда: CBW
· Назначение: Конвертирование байта в слово
· Процессор: 8086
· Команда: CWDE
· Назначение: Конвертирование слова в двойное слово
· Процессор: 80386

CBW расширяет байт, находящийся в регистре AL, до слова в АХ, CWDE расширяет слово в АХ до двойного слова в ЕАХ. CWDE и CWD отличаются тем, что CWDE располагает свой результат в ЕАХ, в то время как CWD, команда, выполняющая точно такое же действие, располагает результат в паре регистров DX:AX. Так же как и команды CWD/CDQ, расширение выполняется путем установки каждого бита старшей половины результата равным старшему биту исходного байта или слова, то есть:

mov al,0F5h; AL = 0F5h = 245 = -11

cbw          ; теперь АХ = 0FFF5h = 65 525 = -11

Примечание: Так же как и в случае с командами PUSHA/PUSHAD, пара команд CWD/CDQ — это одна команда с кодом 99h, и пара команд CBW/CWDE — одна команда с кодом 98h. Интерпретация этих команд зависит от того, в каком (16-битном или в 32-битном) сегменте они исполняются, и точно так же, если указать CDQ или CWDE в 16-битном сегменте, ассемблер поставит префикс изменения разрядности операнда.

· Команда: MOWSX приемник, источник
· Назначение: Пересылка с расширением знака
· Процессор: 80386

Копирует содержимое источника (регистр или переменная размером в байт или слово) в приемник (16- или 32-битный регистр) и расширяет знак аналогично командам CBW/CWDE.

· Команда: MOWZX приемник, источник
· Назначение: Пересылка с расширением нулями
· Процессор: 80386

Копирует содержимое источника (регистр или переменная размером в байт или слово) в приемник (16- или 32-битный регистр) и расширяет нулями, то есть команда

movzx ax,bl

эквивалентна паре команд

mov al,bl

mov ah,0

· Команда: XLAT адрес XLATB
· Назначение: Трансляция в соответствии с таблицей
· Процессор: 8086

Помещает в AL байт из таблицы в памяти по адресу ES:BX (или ES:EBX) со смещением относительно начала таблицы, равным AL. В качестве аргумента для XLAT в ассемблере можно указать имя таблицы, но эта информация никак не используется процессором и служит только как комментарий. Если этот комментарий не нужен, можно применить форму записи XLATB. В качестве примера использования XLAT можно написать следующий вариант преобразования шестнадцатеричного числа в ASCII-код соответствующего ему символа:

mov al,0Ch

mov bx, offset htable

xlatb

если в сегменте данных, на который указывает регистр ES, было записано

htable db "0123456789ABCDEF"

то теперь AL содержит не число 0Сh, а ASCII-код буквы «С». Разумеется, это преобразование можно выполнить, используя гораздо более компактный код всего из трех арифметических команд, который будет рассмотрен в описании команды DAS, но с XLAT можно выполнять любые преобразования такого рода.

· Команда: LEA приемник, источник
· Назначение: Вычисление эффективного адреса
· Процессор: 8086

Вычисляет эффективный адрес источника (переменная) и помещает его в приемник (регистр). С помощью LEA можно вычислить адрес переменной, которая описана сложным методом адресации, например по базе с индексированием. Если адрес 32-битный, а регистр-приемник 16-битный, старшая половина вычисленного адреса теряется, если наоборот, приемник 32-битный, а адресация 16-битная, то вычисленное смещение дополняется нулями.

Примечание: Команду LEA часто используют для быстрых арифметических вычислений, например умножения:

lea bx,[ebx+ebx*4]; ВХ=ЕВХ*5

или сложения:

lea ebx,[eax+12]; ЕВХ=ЕАХ+12

(эти команды меньше, чем соответствующие MOV и ADD, и не изменяют флаги)

Двоичная арифметика

Все команды из этого раздела, кроме команд деления и умножения, изменяют флаги OF, SF, ZF, AF, CF, PF в соответствии с назначением каждого из этих флагов (см. главу 2.1.4).

· Команда: ADD приемник, источник
· Назначение: Сложение
· Процессор: 8086

Команда выполняет арифметическое сложение приемника и источника, помещает сумму в приемник, не изменяя содержимое источника. Приемник может быть регистром или переменной, источник может быть числом, регистром или переменной, но нельзя использовать переменную одновременно и для источника, и для приемника. Команда ADD никак не различает числа со знаком и без знака, но, употребляя значения флагов CF (перенос при сложении чисел без знака), OF (перенос при сложении чисел со знаком) и SF (знак результата), можно использовать ее и для тех, и для других.

· Команда: ADC приемник, источник
· Назначение: Сложение с переносом
· Процессор: 8086

Эта команда во всем аналогична ADD, кроме того, что она выполняет арифметическое сложение приемника, источника и флага СF. Пара команд ADD/ADC используется для сложения чисел повышенной точности. Сложим, например, два 64-битных целых числа: пусть одно из них находится в паре регистров EDX:EAX (младшее двойное слово (биты 0 – 31) — в ЕАХ и старшее (биты 32 – 63) — в EDX), а другое — в паре регистров ЕВХ:ЕСХ:

add eax,ecx adc edx,ebx

Если при сложении младших двойных слов произошел перенос из старшего разряда (флаг CF = 1), то он будет учтен следующей командой ADC.

· Команда: XADD приемник, источник
· Назначение: Обменять между собой и сложить
· Процессор: 80486

Выполняет сложение, помещает содержимое приемника в источник, — сумму операндов — в приемник. Источник всегда регистр, приемник может быть регистром и переменной.

· Команда: SUB приемник, источник
· Н

Поделиться с друзьями:

Папиллярные узоры пальцев рук - маркер спортивных способностей: дерматоглифические признаки формируются на 3-5 месяце беременности, не изменяются в течение жизни...

Типы оградительных сооружений в морском порту: По расположению оградительных сооружений в плане различают волноломы, обе оконечности...

Адаптации растений и животных к жизни в горах: Большое значение для жизни организмов в горах имеют степень расчленения, крутизна и экспозиционные различия склонов...

История создания датчика движения: Первый прибор для обнаружения движения был изобретен немецким физиком Генрихом Герцем...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.129 с.