Assembler - язык неограниченных возможностей

         

Биты, байты и слова


Минимальная единица информации называется битом. Бит может принимать только два значения — обычно 0 и 1. На самом деле эти значения совершенно необязательны — один бит может принимать значения «да» и «нет», показывать присутствие и отсутствие жесткого диска, является ли персонаж игры магом или воином — важно лишь то, что бит имеет только два значения. Но далеко не все величины принимают только два значения, а значит, для их описания нельзя обойтись одним битом.


Рис. 1. Байт

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

Так как всего в байте восемь бит, байт может принимать до 28 = 256 разных значений. Байт используют для представления целых чисел от 0 до 255 (тип unsigned char в С), целых чисел со знаком от -128 до +127 (тип signed char в С), набора символов ASCII (тип char в С) или переменных, принимающих менее 256 значений, например для представления десятичных чисел от 0 до 99. Следующий по размеру базовый тип данных — слово. Размер одного слова в процессорах Intel — два байта.


Рис. 2. Слово

Биты с 0 по 7 составляют младший байт слова, а биты с 8 по 15 — старший. В слове содержится 16 бит, а значит, оно может принимать до 216 = 65 536 разных значений. Слова используют для представления целых чисел без знака со значениями 0 — 65 535 (тип unsigned short в С), целых чисел со знаком со значениями от -32 768 до +32 767 (тип short int в С), адресов сегментов и смещений при 16-битной адресации. Два слова подряд образуют двойное слово, состоящее из 32 бит, а два двойных слова составляют одно учетверенное слово (64 бита). Байты, слова и двойные слова — основные типы данных, с которыми мы будем работать.



Числа со знаком


Легко использовать байты, слова или двойные слова для представления целых положительных чисел — от 0 до 255, 65 535 или 4 294 967 295 соответственно. Чтобы использовать те же самые байты или слова для представления отрицательных чисел, существует специальная операция, известная как дополнение до двух. Для изменения знака числа выполняют инверсию, то есть заменяют в двоичном представлении числа все единицы нулями и нули единицами, а затем прибавляют 1. Например, пусть используются переменные типа слова:

150 = 0096h = 0000 0000 1001 0110b инверсия дает: 1111 1111 0110 1001b +1 = 1111 1111 0110 1010b = 0FF6Ah

Проверим, что полученное число на самом деле -150: сумма с +150 должна, быть равна нулю:

+150 + (-150) = 0096h + FF6Ah = 10000h ;

Единица в l6-м разряде не помещается в слово, и значит, мы действительно получили 0. В этом формате старший (7-й, 15-й, 31-й для байта, слова, двойного слова соответственно) бит всегда соответствует знаку числа 0 — для положительных и 1 — для отрицательных. Таким образом, схема с использованием дополнения до двух выделяет для положительных и отрицательных чисел равные диапазоны: -128 — +127 — для байта, -32 768 — +32 767 — для слов, -2 147 483 648 — +2 147 483 647 — для двойных слов.



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


Прежде всего вам потребуется ассемблер. Здесь самое время сказать, что на самом деле язык программирования, которым мы собираемся заниматься, называется «язык ассемблера» (assembly language). Ассемблер — это программа, которая переводит текст с языка, понятного человеку, в язык, понятный процессору, то есть говорят, что она переводит язык ассемблера в машинный код. Однако сначала в повседневной речи, а затем и в литературе слово «ассемблер» стало также и названием самого языка программирования. Понятно, что, когда говорят «программа на ассемблере», имеют в виду язык, а когда говорят «макроассемблер версии 6.13», имеют в виду программу. Вместе с ассемблером обязательно должна быть еще одна программа — компоновщик (linker), которая и создает исполнимые файлы из одного или нескольких объектных модулей, полученных после запуска ассемблера. Помимо этого для разных целей могут потребоваться дополнительные вспомогательные программы — компиляторы ресурсов, расширители DOS и тому подобное (см. табл. 1).



Microsoft Borland Watcom
DOS, 16 бит masm или ml,
link (16 бит)
tasm
tlink
wasm
wlink
DOS, 32 бита masm или ml,
link (32 бита) и dosx
link (16 бит) и dos32
tasm
tlink
wdosx или dos32
wasm
wlink
dos4gw, pmodew,
zrdx или wdosx
Windows EXE masm386 или ml,
link (32 бита)
rc
tasm
tlink32
brcc32
wasm
wlink
wrc
Windows DLL masm386 или ml,
link (32 бита)
tasm
tlink32
implib
wasm
wlink
wlib

Таблица 1. Ассемблеры и сопутствующие программы

Трудно говорить о том, продукция какой из этих трех компаний однозначно лучше. С точки зрения удобства компиляции TASM лучше подходит для создания 16-битных программ для DOS, WASM — для 32-битных программ для DOS, MASM — для Windows. С точки зрения удобства программирования развитость языковых средств растет в ряду WASM—MASM—TASM. Все примеры программ в этой книге построены так, что можно использовать любой из этих компиляторов.


Разумеется, существуют и другие компиляторы, например бесплатно распространяемый в сети Internet NASM или условно бесплатный А86, но пользоваться ими проще, если вы уже знаете турбо- или макроассемблер. Бесплатно распространяемый GNU ассемблер, gas, вообще использует совершенно непохожий синтаксис, который будет рассмотрен в главе 11, рассказывающей о программировании для UNIX.

Во всех программах встречаются ошибки. Если вы собираетесь не только попробовать примеры из книги, но и написать что-то свое, то вам рано или поздно обязательно потребуется отладчик. Кроме поиска ошибок отладчики иногда применяют и для того, чтобы исследовать работу существующих программ. Безусловно, самый мощный отладчик на сегодняшний день — SoftICE от NuMega Software. Это фактически единственный отладчик для Windows 95/NT, позволяющий исследовать все — от ядра Windows до программ на С++, поддерживающий одновременно 16- и 32-битный код и многое другое. Другие популярные отладчики, распространяемые вместе с соответствующими ассемблерами, — Codeview (MS), Turbo Debugger (Borland) и Watcom Debugger (Watcom).

Еще одна особенность ассемблера, отличающая его от всех остальных языков программирования, — возможность дизассемблирования. То есть, имея исполнимый файл, с помощью специальной программы (дизассемблера) почти всегда можно получить исходный текст на ассемблере. Например, можно дизассемблировать BIOS вашего компьютера и узнать, как выполняется переключение видеорежимов, или драйвер для DOS, чтобы написать такой же для Windows. Дизассемблер не необходим, но иногда оказывается удобно иметь его под рукой. Лучшие дизассемблеры на сегодняшний день — Sourcer от V Communications и IDA.

И наконец, последняя необязательная, но крайне полезная утилита — шестнадцатеричный редактор. Многие такие редакторы (hiew, proview, Iview, hexit) тоже имеют встроенный дизассемблер, так что можно, например, открыв в таком редакторе свою программу, посмотреть, как скомпилировался тот или иной участок программы, поправить какую-нибудь команду ассемблера или изменить значения констант и тут же, без перекомпиляции, запустить программу, чтобы посмотреть на результат изменений.

Двоичная система счисления


Практически все существующие сейчас компьютерные системы, включая Intel, используют для всех вычислений двоичную систему счисления. В их электрических цепях напряжение может принимать два значения, и эти значения назвали нулем и единицей. Двоичная система счисления как раз и использует только эти две цифры, а вместо степеней десяти, как в обычной десятичной системе, здесь используют степени двойки. Чтобы перевести двоичное число в десятичное, надо сложить двойки в степенях, соответствующих позициям, где в двоичном стоят единицы. Например:

10010110b = 1*27+0*26+0*25+1*24+0*23+1*22+1*21+0*20 =
= 128+16+4+2 = 150

Чтобы перевести десятичное число в двоичное, можно, например, просто делить его на 2, записывая 0 каждый раз, когда число делится на два, и 1, когда не делится (табл. 2).

Остаток Разряд
150/2 = 75
75/2 = 37
37/2 = 18
18/2 = 9
9/2 = 4
4/2 = 2
2/2 = 1
1/2 = 0
0
1
1
0
1
0
0
1
0
1
2
3
4
5
6
7
Результат: 10010110b

Таблица 2. Перевод числа из десятичной системы в двоичную

Чтобы отличать двоичные числа от десятичных, в ассемблерных программах в конце каждого двоичного числа ставят букву «b».



Коды символов


Для представления всех букв, цифр и знаков, появляющихся на экране компьютера, обычно используется всего один байт. Символы, соответствующие значениям от 0 до 127, то есть первой половине всех возможных значений байта, были стандартизованы и названы символами ASCII (хотя часто кодами ASCII называют всю таблицу из 256 символов). Сюда входят некоторые управляющие коды (символ с кодом 0Dh — конец строки), знаки препинания, цифры (символы с кодами 30h – 39h), большие (41h – 5Ah) и маленькие (61h – 7Ah) латинские буквы. Вторая половина символьных кодов используется для алфавитов других языков и псевдографики, и набор и порядок символов в ней различаются в разных странах и даже в пределах одной страны. Например, для букв одного только русского языка существует пять разных вариантов размещения во второй половине таблицы символов ASCII. Эти таблицы символов приведены в приложении 1. Существует также стандарт, использующий слова для хранения кодов символов, известный как UNICODE или UCS-2, и даже двойные слова (UCS-4), но мы пока не будем на нем останавливаться.



Логические операции


Один из широко распространенных вариантов значений, которые может принимать один бит, — это значения «правда» и «ложь», используемые в логике, откуда происходят так называемые «логические операции» над битами. Так, если объединить «правду» и «правду» — получится «правда», а если объединить «правду» и «ложь» — «правды» не получится. В ассемблере нам встретятся четыре основные операции — И (AND), ИЛИ (OR), исключающее ИЛИ (XOR) и отрицание (NOT), действие которых приводится в таблице 4.

И ИЛИ ИСКЛЮЧАЮЩЕЕ ИЛИ НЕ
0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1
0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1
0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0
NOT 0 = 1
NOT 1 = 0

Таблица 4. Логические операции

Все эти операции побитовые, поэтому, чтобы выполнить логическую операцию над числом, надо перевести его в двоичный формат и выполнить операцию над каждым битом, например:

96h AND 0Fh = 10010110b AND 00001111b = 00000110b = 06h



Организация памяти


Память с точки зрения процессора представляет собой последовательность байт, каждому из которых присвоен уникальный адрес. Он может принимать значения от 0 до 232-1 (4 гигабайта). Программы же могут работать с памятью как с одним непрерывным массивом (модель памяти flat) или как с несколькими массивами (сегментированные модели памяти). Во втором случае для задания адреса любого байта требуется два числа — адрес начала массива и адрес искомого байта внутри массива. Помимо основной памяти программы могут использовать регистры — специальные ячейки памяти, расположенные физически внутри процессора, доступ к которым осуществляется не по адресам, а по именам. Но здесь мы вплотную подходим к рассмотрению собственно работы процессора, подробнее о чем — в следующей главе.



Представление данных в компьютерах


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



Шестнадцатеричная система счисления


Главное неудобство двоичной системы счисления — это размеры чисел, с которыми приходится обращаться. На практике с двоичными числами работают, только если необходимо следить за значениями отдельных бит, а когда размеры переменных превышают хотя бы четыре бита, используется шестнадцатеричная система. Эта система хороша тем, что она гораздо более компактна, компактнее десятичной, и тем, что перевод в двоичную систему и обратно происходит очень легко. В шестнадцатеричной системе используется 16 «цифр»: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, А, В, С. D, E, F, и номер позиции цифры в числе соответствует степени, в которую надо возвести число 16, так что:

96h = 9 * 16 + 6 = 150

Перевод в двоичную систему и обратно осуществляется крайне просто — вместо каждой шестнадцатеричной цифры подставляют соответствующее четырехзначное двоичное число:

9h = 1001b, 6h = 0110b, 96h = 10010110b

В ассемблерных программах при записи чисел, начинающихся с А, В, С, D, E, F, в начале приписывается цифра 0, чтобы нельзя было спутать такое число с названием переменной или другим идентификатором. После шестнадцатеричных чисел ставится буква «h» (см. табл. 3).

Десятичное Двоичное Шестнадцатиричное
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0000b
0001b
0010b
0011b
0100b
0101b
0110b
0111b
1000b
1001b
1010b
1011b
1100b
1101b
1110b
1111b
10000b
00h
01h
02h
03h
04h
05h
06h
07h
08h
09h
0Ah
0Bh
0Ch
0Dh
0Eh
0Fh
10h

Таблица 3. Двоичные и шестнадцатеричные числа



Адресация по базе с индексированием


В этом методе адресации смещение операнда в памяти вычисляется как сумма чисел, содержащихся в двух регистрах, и смещения, если оно указано. Все следующие команды — это разные формы записи одного и того же действия:

mov ax,[bx+si+2] mov ax,[bx][si]+2 mov ax,[bx+2][si] mov ax,[bx][si+2] mov ax,2[bx][si]

В регистр AX помещается слово из ячейки памяти со смещением, равным сумме чисел, содержащихся в BX и SI, и числа 2. Из шестнадцатибитных регистров так можно складывать только BX + SI, BX + DI, BP + SI и BP + DI, а из 32-битных — все восемь регистров общего назначения. Так же как и для прямой адресации, вместо непосредственного указания числа можно использовать имя переменной, заданной одной из директив определения данных. Так можно прочитать, например, число из двумерного массива: если задана таблица 10x10 байт, 2 — смещение ее начала от начала сегмента данных (на практике будет использоваться имя этой таблицы), BX = 20, а SI = 7, приведенные команды прочитают слово, состоящее из седьмого и восьмого байт третьей строки. Если таблица состоит не из одиночных байт, а из слов или двойных слов, удобнее использовать следующую, наиболее полную форму адресации.



Адресация по базе с индексированием и масштабированием


Это самая полная возможная схема адресации, в которую входят все случаи, рассмотренные ранее, как частные. Полный адрес операнда можно записать как выражение, представленное на рис. 6.


Рис. 6. Полная форма адресации

Смещение может быть байтом или двойным словом. Если ESP или EBP используются в роли базового регистра, селектор сегмента операнда берется по умолчанию из регистра SS, во всех остальных случаях — из DS.



Адресация по базе со сдвигом


Теперь скомбинируем два предыдущих метода адресации: следующая команда

mov ax,[bx+2]

помещает в регистр AX слово, находящееся в сегменте, указанном в DS, со смещением на 2 большим, чем число, находящееся в BX. Так как слово занимает ровно два байта, эта команда поместила в AX слово, непосредственно следующее за тем, которое есть в предыдущем примере. Такая форма адресации используется в тех случаях, когда в регистре находится адрес начала структуры данных, а доступ надо осуществить к какому-нибудь элементу этой структуры. Другое важное применение адресации по базе со сдвигом — доступ из подпрограммы к параметрам, переданным в стеке, используя регистр BP (EBP) в качестве базы и номер параметра в качестве смещения, что детально разобрано в параграфе 5.2. Другие допустимые формы записи этого способа адресации:

mov ax,[bp]+2 mov ax,2[bp]

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



Арифметические операции ММХ


Команда: PADDB приемник,источник
PADDW приемник,источник
PADDD приемник,источник
Назначение: Сложение
Процессор: ММХ

Команды выполняют сложение отдельных элементов данных (байт — для PADDB, слов — для PADDW, двойных слов — для PADDD) источника (регистр ММХ или переменная) и соответствующих элементов приемника (регистр ММХ). Если при сложении возникает перенос, он не влияет ни на следующие элементы, ни на флаг переноса, а просто игнорируется (так что, например, для PADDB 255 + 1 = 0, если это числа без знака, или -128 + -1 = +127, если со знаком).

Команда: PADDSB приемник,источник
PADDSW приемник,источник
Назначение: Сложение с насыщением
Процессор: ММХ

Команды выполняют сложение отдельных элементов данных (байт — для PADDSB и слов — для PADDSW) источника (регистр ММХ или переменная) и соответствующих элементов приемника (регистр ММХ). Если результат сложения выходит за пределы байта со знаком для PADDSB (больше +127 или меньше -128) или слова со знаком для PADDSW (больше +32 767 или меньше -32 768), в качестве результата используется соответствующее максимальное или минимальное число, так что, например, для PADDSB -128 + -1 = -128.

Команда: PADDUSB приемник,источник
PADDUSW приемник,источник
Назначение: Беззнаковое сложение с насыщением
Процессор: ММХ

Команды выполняют сложение отдельных элементов данных (байт — для PADDUSB и слов — для PADDUSW) источника (регистр ММХ или переменная) и соответствующих элементов приемника (регистр ММХ). Если результат сложения выходит за пределы байта без знака для PADDUSB (больше 255 или меньше 0) или слова без знака для PADDUSW (больше 65 535 или меньше 0), в качестве результата используется соответствующее максимальное или минимальное число, так что, например, для PADDUSB 255 + 1 = 255.

Команда: PSUBB приемник,источник
PSUBW приемник,источник
PSUBD приемник,источник
Назначение: Вычитанние
Процессор: ММХ

/p> Команды выполняют вычитание отдельных элементов данных (байт — для PSUBB, слов — для PSUBW, двойных слов — для PSUBD) источника (регистр ММХ или переменная) и соответствующих элементов приемника (регистр ММХ). Если при вычитании возникает заем, он игнорируется (так что, например, для PSUBB -128 - 1 = +127 — для чисел со знаком или 0 - 1 = 255 — для чисел без знака).

Команда: PSUBSB приемник,источник
PSUBSW приемник,источник
Назначение: Вычитание с насыщением
Процессор: ММХ
Команды выполняют вычитание отдельных элементов данных (байт — для PSUBSB и слов — для PSUBSW) источника (регистр ММХ или переменная) и соответствующих элементов приемника (регистр ММХ). Если результат вычитания выходит за пределы байта или слова со знаком, в качестве результата используется соответствующее максимальное или минимальное число, так что, например, для PSUBSB -128 - 1 = -128.

Команда: PSUBUSB приемник,источник
PSUBUSW приемник,источник
Назначение: Беззнаковое вычитание с насыщением
Процессор: ММХ
Команды выполняют вычитание отдельных элементов данных (байт — для PSUBUSB и слов — для PSUBUSW) источника (регистр ММХ или переменная) и соответствующих элементов приемника (регистр ММХ). Если результат вычитания выходит за пределы байта или слова без знака, в качестве результата используется соответствующее максимальное или минимальное число, так что, например, для PSUBUSB 0 - 1 = 0.

Команда: PMULHW приемник,источник
Назначение: Старшее умножение
Процессор: ММХ
Команда умножает каждое из четырех слов со знаком из источника (регистр ММХ или переменная) на соответствующее слово со знаком из приемника (регистр ММХ). Старшее слово каждого из результатов записывается в соответствующую позицию приемника.

Команда: PMULLW приемник,источник
Назначение: Младшее умножение
Процессор: ММХ
<


/p> Умножает каждое из четырех слов со знаком из источника (регистр ММХ или переменная) на соответствующее слово со знаком из приемника (регистр ММХ). Младшее слово каждого из результатов записывается в соответствующую позицию приемника.

Команда: PMADDWD приемник,источник
Назначение: Умножение и сложение
Процессор: ММХ
Умножает каждое из четырех слов со знаком из источника (регистр ММХ или переменная) на соответствующее слово со знаком из приемника (регистр ММХ). Произведения двух старших пар слов складываются между собой, и их сумма записывается в старшее двойное слово приемника. Сумма произведений двух младших пар слов записывается в младшее двойное слово.


Базовая арифметика FPU


Команда: FADD приемник,источник
Назначение: Сложение вещественных чисел
Команда: FADDP приемник,источник
Назначение: Сложение с выталкиванием из стека
Команда: FIADD источник
Назначение: Сложение целых чисел
Процессор: 8087

Команда выполняет сложение источника и приемника и помещает результат в приемник. Команда FADDP после этого выталкивает ST(0) из стека (помечает ST(0) как пустой и увеличивает ТОР на один). Команды сложения могут принимать следующие формы:

FADD источник, когда источником является 32- или 64-битная переменная, а приемником — ST(0);

FADD ST(0),ST(n), FADD ST(n),ST(0), FADDP ST(n),ST(0), когда источник и приемник заданы явно в виде регистров FPU;

FADD без операндов — эквивалентно FADD ST(0),ST(1); FADDP без операндов — эквивалентно FADDP ST(1),ST(0);

FIADD источник, когда источником является 16- или 32-битная переменная, содержащая целое число, а приемником — ST(0).

Команда: FSUB приемник,источник
Назначение: Вычитание вещественных чисел
Команда: FSUBP приемник,источник
Назначение: Вычитание с выталкиванием из стека
Команда: FISUB источник
Назначение: Вычитание целых чисел
Процессор: 8087

Выполняет вычитание источника из приемника и сохраняет результат в приемнике. Команда FSUBP после этого выталкивает ST(0) из стека (помечает ST(0) как пустой и увеличивает ТОР на один). Команды вычитания могут принимать следующие формы:

FSUB источник, когда источником является 32- или 64-битная переменная, содержащая вещественное число, а приемником — ST(0);

FSUB ST(0),ST(n), FSUB ST(n),ST(0), FSUBP ST(n),ST(0), когда источник и приемник заданы явно в виде регистров FPU;

FSUB без операндов — эквивалентно FSUB ST(0),ST(1); FSUBP без операндов — эквивалентно FSUBP ST(1),ST(0);

FISUB источник, когда источником является 16- или 32-битная переменная, содержащая целое число, а приемником — ST(0).



Числа с плавающей запятой


В процессорах Intel все операции с плавающей запятой выполняет специальное устройство, FPU (Floating Point Unit), с собственными регистрами и собственным набором команд, поставлявшееся сначала в виде сопроцессора (8087, 80287, 80387, 80487), а начиная с 80486DX — встраивающееся в основной процессор. FPU полностью соответствует стандартам IEEE 754 и (начиная с 80486) IEEE 854.



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


Процессоры Intel поддерживают операции с двумя форматами десятичных чисел: неупакованное двоично-десятичное число — байт, принимающий значения от 00 до 09, и упакованное двоично-десятичное число — байт, принимающий значения от 00 до 99h. Все обычные арифметическиe операции над такими числами приводят к неправильным результатам. Например, если увеличить 19h на 1, то получится число 1Ah, а не 20h. Для коррекции результатов арифметических действий над двоично-десятичными числами используются следующие команды.

Команда: DAA
Назначение: BCD-коррекция после сложения
Процессор: 8086

Если эта команда выполняется сразу после ADD (ADC, INC или XADD) и в регистре AL находится сумма двух упакованных двоично-десятичных чисел, то в результате в AL записывается упакованное двоично-десятичное число, которое должно было быть результатом сложения. Например, если AL содержит число 19h, последовательность команд

inc al daa

приведет к тому, что в AL окажется 20h (а не 1Ah, как было бы после INC).

DAA выполняет следующие действия:

Если младшие четыре бита AL больше 9 или флаг AF = 1, то AL увеличивается на 6, CF устанавливается, если при этом сложении произошел перенос, и AF устанавливается в 1.

Иначе AF = 0.

Если теперь старшие четыре бита AL больше 9 или флаг CF = 1, то AL увеличивается на 60h и CF устанавливается в 1.

Иначе CF = 0.

Флаги AF и CF устанавливаются, если в ходе коррекции происходил перенос из первой или второй цифры соответственно, SF, ZF и PF устанавливаются в соответствии с результатом, флаг OF не определен.

Команда: DAS
Назначение: BCD-коррекция после вычитания
Процессор: 8086

Если эта команда выполняется сразу после SUB (SBB или DEC) и в регистре AL находится разность двух упакованных двоично-десятичных чисел, то в результате в AL записывается упакованное двоично-десятичное число, которое должно было быть результатом вычитания. Например, если AL содержит число 20h, последовательность команд

dec al das


приведет к тому, что в AL окажется 19h ( а не 1Fh, как было бы после DEC).

DAS выполняет следующие действия:

Если младшие четыре бита AL больше 9 или флаг AF = 1, то AL уменьшается на 6, CF устанавливается, если при этом вычитании произошел заем, и AF устанавливается в 1.

Иначе AF = 0.

Если теперь старшие четыре бита AL больше 9 или флаг CF = 1, то AL уменьшается на 60h и CF устанавливается в 1.

Иначе CF = 0.

Известный пример необычного использования этой команды — самый компактный вариант преобразования шестнадцатеричной цифры в ASCII-код соответствующего символа (более длинный и очевидный вариант этого преобразования рассматривался в описании команды XLAT):

cmp al,10 sbb al,96h das

После SBB числа 0 – 9 превращаются в 96h – 9Fh, а числа 0Ah – 0Fh — в 0А1h – 0A6h. Затем DAS вычитает 66h из первой группы чисел, переводя их в 30h – 39h, и 60h из второй группы чисел, переводя их в 41h – 46h.
Флаги AF и CF устанавливаются, если в ходе коррекции происходил заем из первой или второй цифры соответственно, SF, ZF и PF устанавливаются в соответствии с результатом, флаг OF не определен.

Команда: AAA
Назначение: ASCII-коррекция после сложения
Процессор: 8086
Корректирует сумму двух неупакованных двоично-десятичных чисел в AL. Если коррекция приводит к десятичному переносу, АН увеличивается на 1. Эта команда имеет смысл сразу после команды сложения двух таких чисел. Например, если при сложении 05 и 06 в АХ окажется число 000Bh, то команда ААА скорректирует его в 0101h (неупакованное десятичное 11). Флаги CF и OF устанавливаются в 1, если произошел перенос из AL в АН, иначе они равны нулю. Значения флагов OF, SF, ZF и PF не определены.

Команда: AAS
Назначение: ASCII-коррекция после вычитания
Процессор: 8086
Корректирует разность двух неупакованных двоично-десятичных чисел в AL сразу после команды SUB или SBB. Если коррекция приводит к займу, АН уменьшается на 1. Флаги CF и OF устанавливаются в 1, если произошел заем из AL в АН, и в ноль — в противном случае. Значения флагов OF, SF, ZF и PF не определены.

Команда: AAM
Назначение: ASCII-коррекция после умножения
Процессор: 8086
<


/p> Корректирует результат умножения неупакованных двоично-десятичных чисел, находящийся в АХ после выполнения команды MUL, преобразовывая полученный результат в пару неупакованных двоично-десятичных чисел (в АН и AL). Например:

mov al,5 mov bl,5 ; умножить 5 на 5 mul bl ; результат в АХ - 0019h aam ; теперь АХ содержит 0205h

ААМ устанавливает флаги SF, ZF и PF в соответствии с результатом и оставляет OF, AF и CF неопределенными.

Код команды ААМ — D4h 0Ah, где 0Ah — основание системы счисления, по отношению к которой выполняется коррекция. Этот байт можно заменить на любое другое число (кроме нуля), и ААМ преобразует АХ к двум неупакованным цифрам любой системы счисления. Такая обобщенная форма ААМ работает на всех процессорах (начиная с 8086), но появляется в документации Intel только с процессоров Pentium. Фактически действие, которое выполняет ААМ, — целочисленное деление AL на 0Ah (или любое другое число в общем случае), частное помещается в AL, и остаток — в АН, так что эту команду часто используют для быстрого деления в высокооптимизированных алгоритмах.
Команда: AAD
Назначение: ASCII-коррекция перед делением
Процессор: 8086
Выполняет коррекцию неупакованного двоично-десятичного числа, находящегося в регистре АХ, так, чтобы последующее деление привело к корректному десятичному результату. Например, разделим десятичное 25 на 5:

mov ax,0205h ; 25 в неупакованном формате mov bl,5 aad ; теперь в АХ находится 19h div bl ; АХ = 0005

Флаги SF, ZF и PF устанавливаются в соответствии с результатом, OF, AF и CF не определены.

Так же как и команда ААМ, AAD используется с любой системой счисления: ее код — D5h 0Ah, и второй байт можно заменить на любое другое число. Действие AAD состоит в том, что содержимое регистра АН умножается на второй байт команды (0Ah по умолчанию) и складывается с AL, после чего АН обнуляется, так что AAD можно использовать для быстрого умножения на любое число.

Другие команды


Команда: NOP
Назначение: Отсутствие операции
Процессор: 8086

NOP — однобайтная команда (код 90h), которая не выполняет ничего, только занимает место и время. Код этой команды фактически соответствует команде XCHG AL,AL. Можно многие команды записать так, что они не будут приводить ни к каким действиям, например:

mov ax,ax ; 2 байта xchg ax,ax ; 2 байта lea bx,[bx+0] ; 3 байта (8Dh, 5Fh, 00h, но многие ; ассемблеры, встретив такую команду, ; реально используют более короткую команду ; lea bx,[bx] с кодом 8Dh 1Fh) shl eax,0 ; 4 байта shrd еах,еах,0 ; 5 байт

Префикс: LOCK
Назначение: Префикс блокировки шины данных
Процессор: 8086

На все время выполнения команды, снабженной таким префиксом, будет заблокирована шина данных, и если в системе присутствует другой процессор, он не сможет обращаться к памяти, пока не закончится выполнение команды с префиксом LOCK. Команда XCHG автоматически всегда выполняется с блокировкой доступа к памяти, даже если префикс LOCK не указан. Этот префикс можно использовать только с командами ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD и XCHG.

Команда: UD2
Назначение: Неопределенная операция
Процессор: P6

Эта команда всегда вызывает ошибку «неопределенная операция» (исключение #UD). Впервые она описана как таковая для Pentium Pro, но во всех предыдущих процессорах эта команда (код 0Fh 0Bh) не была определена и, естественно, приводила к такой же ошибке. UD2 предназначена для тестирования программного обеспечения, в частности операционных систем, которые должны уметь корректно обрабатывать такую ошибку. Название команды происходит от команды UD (код 0Fh 0FFh), которая была определена AMD для процессоров AMD K5.

Команда: CPUID
Назначение: Идентификация процессора
Процессор: 80486

CPUID сообщает информацию о производителе, типе и модификации процессора, о наличии и поддержке различных расширений. Команда CPUID поддерживается Intel, начиная с процессоров Intel 80486DX/SX/DX2 SL, UMC U5S, Cyrix M1, AMD 80486DX4. Чтобы проверить, поддерживает ли процессор эту команду, попробуйте установить флаг ID в 1 (бит 21 в регистре EFLAGS) — если это получается, значит, команда CPUID поддерживается.


Результат работы CPUID зависит от значения регистра ЕАХ. Если ЕАХ = 0, CPUID возвращает в ЕАХ максимальное значение, с которым ее можно вызывать (2 для Р6, 1 для Р5), а регистры EBX:ECX:EDX содержат 12-байтную строку — идентификатор производителя (табл. 8).

Таблица 8. Строки производителей в CPUID

Производитель Строка в ЕВХ:ЕСХ:ЕРХ
Intel GenuineIntel
UMC UMC UMC UMC
Cyrix CyrixInstead
AMD AuthenticAMD
NexGen NexGenDriven
Centaur Technology CentaurHalls
Например, для процессоров Intel регистр ЕВХ содержит «Genu» (756E6547h), ЕСХ содержит «ineI» (49656E69h), a EDX — «ntel» (6C65746Eh).

Если ЕАХ = 1, CPUID возвращает в ЕАХ информацию о версии процессора, а в EDX — информацию о поддерживаемых расширениях. Многие понятия в этом описании относятся к работе процессора в защищенном режиме и рассмотрены ниже.

Биты 3 – 0 ЕАХ — Модификация.

Биты 7 – 4 ЕАХ — Модель.

Биты 11 – 8 ЕАХ — Семейство (3 для 386, 4 для 486, 5 для Pentium, 6 для Pentium Pro).

Биты 13 – 12 ЕАХ — Тип (0 — OEM, 1 — Overdrive, 2 — Dual).

Биты 31 – 14 ЕАХ зарезервированы и равны нулю.

Бит 0 EDX — «FPU» Процессор содержит FPU и может выполнять весь набор команд 80387.

Бит 1 EDX — «VME» Процессор поддерживает усовершенствованный режим V86 (флаги VIF и VIP в EFLAGS, биты VME и PVI в CRO).

Бит 2 EDX — «DE» Процессор поддерживает точки останова по вводу/выводу, бит DE в CR0.

Бит 3 EDX — «PSE» Процессор поддерживает страницы до 4 Мб, бит PSE в CR4, модифицированные биты в элементах списков страниц (РDЕ) и таблиц страниц (РТЕ).

Бит 4 EDX — «TSC» Процессор поддерживает команду RDTSC и бит TSC в CR4.



Бит 5 EDX — «MSR» Процессор поддерживает команды RDMSR и WRMSR и машинно-специфичные регистры, совместимые с Pentium.

Бит 6 EDX — «РАЕ» Процессор поддерживает физические адреса больше 32 бит, дополнительный уровень в таблицах трансляции страниц, страницы по 2 Мб и бит РАЕ в CR4. Число бит для физических адресов зависит от модели процессора. Так, Pentium Pro поддерживает 36 бит.

Бит 6 EDX — «РТЕ» (только для Cyrix).

Бит 7 EDX — «МСЕ» Процессор поддерживает бит МСЕ в CR4.

Бит 8 EDX — «СХ8» Процессор поддерживает команду CMPXCHG8B.

Бит 9 EDX — «APIC» Процессор содержит встроенный контроллер прерываний (APIC), и он активирован и доступен.

Бит 9 EDX — «PGE» (только для AMD).

Бит 10 EDX зарезервирован.

Бит 11 EDX — «SEP» Процессор поддерживает быстрые системные вызовы, команды SYSENTER и SYSEXIT (Pentium II).

Бит 12 EDX — «MTRR» Процессор поддерживает машинно-специфичные регистры MTRR.

Бит 13 EDX — «PGE» Процессор поддерживает бит PGE в CR4 и глобальные флаги в PTDE и РТЕ, указывающие элементы TLB, которые принадлежат сразу нескольким задачам.

Бит 14 EDX — «МСА» Процессор поддерживает машинно-специфичный регистр MCG_CAP.

Бит 15 EDX — «CMOV» Процессор поддерживает команды CMOVcc и (если бит 0 EDX установлен) FCMOVcc (Pentium Pro).

Бит 16 EDX — «PAT» Процессор поддерживает таблицу атрибутов страниц.

Биты 17 – 22 зарезервированы.

Бит 23 EDX — «ММХ» Процессор поддерживает набор команд ММХ.

Бит 24 EDX — «FXSR» Процессор поддерживает команды быстрого чтения/записи (ММХ2).

Биты 31 – 25 EDX зарезервированы.



Если ЕАХ = 2, CPUID на процессорах семейства Р6 возвращает в регистрах ЕАХ, ЕВХ, ЕСХ и EDX информацию о кэшах и TLB. Самый младший байт ЕАХ (регистр AL) указывает, сколько раз надо вызвать CPUID с ЕАХ = 2, чтобы получить информацию обо всех кэшах (1 для Pentium Pro и Pentium II). Самый старший бит (бит 31) каждого регистра указывает, содержит ли этот регистр правильную информацию (бит 31 = 0) или он зарезервирован (бит 31 = 1). В первом случае регистр содержит информацию в 1-байтных дескрипторах со следующими значениями:

00h — Пустой дескриптор.

01h — TLB команд, 4-килобайтные страницы, 4-сторонняя ассоциативность, 32 элемента.

02h — TLB команд, 4-мегабайтные страницы, 4-сторонняя ассоциативность, 4 элемента.

03h — TLB данных, 4-килобайтные страницы, 4-сторонняя ассоциативность, 64 элемента.

04h — TLB данных, 4-мегабайтные страницы, 4-сторонняя ассоциативность, 8 элементов.

06h — Кэш команд, 8 Кб, 4-сторонняя ассоциативность, 32 байта в строке.

08h — Кэш команд, 16 Кб, 4-сторонняя ассоциативность, 32 байта в строке.

0Ah — Кэш данных, 8 Кб, 2-сторонняя ассоциативность, 32 байта в строке.

0Ch — Кэш данных, 16 Кб, 2-сторонняя ассоциативность, 32 байта в строке.

41h — Унифицированный кэш, 128 Кб, 4-сторонняя ассоциативность, 32 байта в строке.

42h — Унифицированный кэш, 256 Кб, 4-сторонняя ассоциативность, 32 байта в строке.

43h — Унифицированный кэш, 512 Кб, 4-сторонняя ассоциативность, 32 байта в строке.

44h — Унифицированный кэш, 1 Мб, 4-сторонняя ассоциативность, 32 байта в строке.

Совместимые с Intel процессоры AMD и Cyrix поддерживают вызов «расширенных функций» CPUID со значениями ЕАХ, в которых самый старший бит всегда установлен в 1.

ЕАХ = 80000000h: Возвращает в ЕАХ максимальный номер расширенной функции CPUID, поддерживаемой данным процессором.



ЕАХ = 80000001h: Возвращает в ЕАХ 051Xh для AMD K5 (X — номер модификации) или 061Хh для AMD К6. В EDX эта функция возвращает информацию о поддерживаемых расширениях (указаны только флаги, отличающиеся от CPUID с ЕАХ = 1).

Бит 5 EDX «MSR» — Процессор поддерживает машинно-специфичные регистры, совместимые с К5.

Бит 10 EDX — Процессор поддерживает команды SYSCALL и SYSRET.

Бит 16 EDX — Процессор поддерживает команды FCMOVcc.

Бит 24 EDX — Процессор поддерживает ММХ с расширениями от Cyrix.

Бит 25 EDX — Процессор поддерживает набор команд AMD 3D.

ЕАХ = 80000002h, 80000003h и 80000004h — последовательный вызов CPUID с этими значениями в ЕАХ возвращает в EAX:EBX:ECX:EDX последовательно четыре 16-байтные части строки — имени процессора. Например: «AMD-K5(tm) Processor».

ЕАХ = 80000005h — Команда возвращает информацию о TLB в регистре ЕВХ (старшее слово — TLB данных, младшее слово — TLB команд, старший байт — ассоциативность, младший байт — число элементов), о кэше данных в регистре ЕСХ и о кэше команд в регистре EDX (биты 31 – 24 — размер в килобайтах, биты 23 – 16 — ассоциативность, биты 15 – 8 — число линий на тэг, биты 7 – 0 — число байт на линию.


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


Все команды из этого раздела, кроме команд деления и умножения, изменяют флаги 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 приемник, источник
Назначение: Вычитание
Процессор: 8086

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

Команда: SBB приемник, источник
Назначение: Вычитание с займом
Процессор: 8086
<
/p> Эта команда во всем аналогична SUB, кроме того, что она вычитает из приемника значение источника и дополнительно вычитает значение флага CF. Так, можно использовать эту команду для вычитания 64-битных чисел в EDX:EAX и ЕВХ:ЕСХ аналогично ADD/ADC:

sub eax,ecx sbb edx,ebx

Если при вычитании младших двойных слов произошел заем, то он будет учтен при вычитании старших.

Команда: IMUL источник
IMUL приемник, источник
IMUL приемник, источник1, источник2
Назначение: Умножение чисел со знаком
Процессор: 8086
80386
80186
Эта команда имеет три формы, различающиеся числом операндов:

IMUL источник: источник (регистр или переменная) умножается на AL, АХ или ЕАХ (в зависимости от размера операнда), и результат располагается в АХ, DX:AX или EDX:EAX соответственно.

IMUL приемник,источник: источник (число, регистр или переменная) умножается на приемник (регистр), и результат заносится в приемник.

IMUL приемник,источник1,источник2: источник 1 (регистр или переменная) умножается на источник 2 (число), и результат заносится в приемник (регистр).

Во всех трех вариантах считается, что результат может занимать в два раза больше места, чем размер источника. В первом случае приемник автоматически оказывается достаточно большим, но во втором и третьем случаях могут произойти переполнение и потеря старших бит результата. Флаги OF и CF будут равны единице, если это произошло, и нулю, если результат умножения поместился целиком в приемник (во втором и третьем случаях) или в младшую половину приемника (в первом случае).

Значения флагов SF, ZF, AF и PF после команды IMUL не определены.

Команда: MUL источник
Назначение: Умножение чисел без знака
Процессор: 8086
Выполняет умножение содержимого источника (регистр или переменная) и регистра AL, АХ, ЕАХ (в зависимости от размера источника) и помещает результат в АХ, DX:AX, EDX:EAX соответственно. Если старшая половина результата (АН, DX, EDX) содержит только нули (результат целиком поместился в младшую половину), флаги CF и OF устанавливаются в 0, иначе — в 1. Значение остальных флагов (SF, ZF, AF и PF) не определено.

Команда: IDIV источник
Назначение: Целочисленное деление со знаком
Процессор: 8086
<


/p> Выполняет целочисленное деление со знаком AL, АХ или ЕАХ (в зависимости от размера источника) на источник (регистр или переменная) и помещает результат в AL, АХ или ЕАХ, а остаток — в АН, DX или EDX соответственно. Результат всегда округляется в сторону нуля, знак остатка всегда совпадает со знаком делимого, абсолютное значение остатка всегда меньше абсолютного значения делителя. Значения флагов CF, OF, SF, ZF, AF и PF после этой команды не определены, а переполнение или деление на ноль вызывает исключение #DE (ошибка при делении) в защищенном режиме и прерывание 0 — в реальном.

Команда: DIV источник
Назначение: Целочисленное деление без знака
Процессор: 8086
Выполняет целочисленное деление без знака AL, АХ или ЕАХ (в зависимости от размера источника) на источник (регистр или переменная) и помещает результат в AL, АХ или ЕАХ, а остаток — в АН, DX или EDX соответственно. Результат всегда округляется в сторону нуля, абсолютное значение остатка всегда меньше абсолютного значения делителя. Значения флагов CF, OF, SF, ZF, AF и PF после этой команды не определены, а переполнение или деление на ноль вызывает исключение #DE (ошибка при делении) в защищенном режиме и прерывание 0 — в реальном.

Команда: INC приемник
Назначение: Инкремент
Процессор: 8086
Увеличивает приемник (регистр или переменная) на 1. Единственное отличие этой команды от ADD приемник,1 состоит в том, что флаг CF не затрагивается. Остальные арифметические флаги (OF, SF, ZF, AF, PF) устанавливаются в соответствии с результатом сложения.

Команда: DEC приемник
Назначение: Декремент
Процессор: 8086
Уменьшает приемник (регистр или переменная) на 1. Единственное отличие этой команды от SUB приемник,1 состоит в том, что флаг CF не затрагивается. Остальные арифметические флаги (OF, SF, ZF, AF, PF) устанавливаются в соответствии с результатом вычитания.

Команда: NEG приемник
Назначение: Изменение знака
Процессор: 8086
<


/p> Выполняет над числом, содержащимся в приемнике (регистр или переменная), операцию дополнения до двух. Эта операция эквивалентна обращению знака операнда, если рассматривать его как число со знаком. Если приемник равен нулю, флаг CF устанавливается в 0, иначе — в 1. Остальные флаги (OF, SF, ZF, AF, PF) устанавливаются в соответствии с результатом операции.

Красивый пример применения команды NEG — получение абсолютного значения числа, используя всего две команды — изменение знака и переход на первую команду еще раз, если знак отрицательный:

label0: neg eax js label0

Команда: CMP приемник, источник
Назначение: Сравнение
Процессор: 8086
Сравнивает приемник и источник и устанавливает флаги. Сравнение осуществляется путем вычитания источника (число, регистр или переменная) из приемника (регистр или переменная; приемник и источник не могут быть переменными одновременно), причем результат вычитания никуда не записывается, единственным результатом работы этой команды оказывается изменение флагов CF, OF, SF, ZF, AF и PF. Обычно команду СМР используют вместе с командами условного перехода (Jcc), условной пересылки данных (CMOVcc) или условной установки байт (SETcc), которые позволяют использовать результат сравнения, не обращая внимания на детальное значение каждого флага. Так, команды CMOVE, JE и SETE выполнят соответствующие действия, если значения операндов предшествующей команды СМР были равны.

Несмотря на то что условные команды почти всегда применяются сразу после СМР, не надо забывать, что точно так же их можно использовать после любой команды, модифицирующей флаги, например: проверить равенство АХ нулю можно более короткой командой

test ax,ax

а равенство единице — однобайтной командой

dec ax

Команда: CMPXCHG приемник, источник
Назначение: Сравнить и обменять между собой
Процессор: 80486
Сравнивает значение, содержащееся в AL, АХ, ЕАХ (в зависимости от размера операндов), с приемником (регистром). Если они равны, содержимое источника копируется в приемник и флаг ZF устанавливается в 1. Если они не равны, содержимое приемника копируется в AL, АХ, ЕАХ и флаг ZF устанавливается в 0. Остальные флаги устанавливаются по результату операции сравнения, как после СМР. Источник всегда регистр, приемник может быть регистром и переменной.

Команда: CMPXCHG8B приемник
Назначение: Сравнить и обменять восемь байт
Процессор: Р5
Выполняет сравнение содержимого регистров EDX:EAX как 64-битного числа (младшее двойное слово в ЕАХ, старшее — в EDX) с приемником (восьмибайтная переменная в памяти). Если они равны, содержимое регистров ЕСХ:ЕВХ как 64-битное число (младшее двойное слово в ЕВХ, старшее — в ЕСХ) копируется в приемник. Иначе содержимое приемника копируется в EDX:EAX.


Исключения FPU


При выполнении команд FPU могут возникать шесть типов особых ситуаций, называемых исключениями. При возникновении исключения соответствующий флаг в регистре SR устанавливается в 1 и, если маска этого исключения в регистре CR не установлена, вызывается обычное прерывание INT 10h (если бит NE в регистре центрального процессора CR0 установлен в 1) или IRQ13 (INT 75h), обработчик которого может прочитать регистр SR, чтобы определить тип исключения (и FIP, и FDP) и команду, которая его вызвала, а затем попытаться исправить ситуацию. Если бит маски наступившего исключения в регистре CR установлен в 1, выполняются следующие действия по умолчанию:

неточный результат: результат округляется в соответствии с битами RC (на самом деле это исключение происходит очень часто. Например: дробь 1/6 не может быть представлена точно десятичным вещественным числом любой точности и округляется). При этом флаг С1 показывает, в какую сторону произошло округление: 0 — вниз, 1 — вверх;

антипереполнение: результат слишком мал, чтобы быть представленным обычным числом, — он преобразуется в денормализованное число;

переполнение: результат преобразуется в бесконечность соответствующего знака;

деление на ноль: результат преобразуется в бесконечность соответствующего знака (учитывается и знак нуля);

денормализованный операнд: вычисление продолжается, как обычно;

недействительная операция: результат определяется из таблицы 12.

Таблица 12. Результаты операций, приводящих к исключениям

Операция Результат
Ошибка стека Неопределенность
Операция с неподдерживаемым числом Неопределенность
Операция с SNAN QNAN
Сравнение числа с NAN C0 = C2 = C3 = 1
Сложение бесконечностей с одним знаком или вычитание  — с разным Неопределенность
Умножение нуля на бесконечность Неопределенность
Деление бесконечности на бесконечность или 0/0 Неопределенность
Команды FPREM и FPREM1, если делитель  — 0 или делимое — бесконечность Неопределенность и C2 = 0
Тригонометрическая операция над бесконечностью Неопределенность и C2 = 0
Корень или логарифм, если x < 0, log(x+1), если x < -1 Неопределенность
FBSTP, если регистр-источник пуст, содержит NAN, бесконечность или превышает 18 десятичных знаков Десятичная неопределенность
FXCH, если один из операндов пуст Неопределенность



Команды передачи управления


Команда: JMP операнд
Назначение: Безусловный переход
Процессор: 8086

JMP передает управление в другую точку программы, не сохраняя какой-либо информации для возврата. Операндом может быть непосредственный адрес для перехода (в программах используют имя метки, установленной перед командой, на которую выполняется переход), а также регистр или переменная, содержащая адрес.

В зависимости от типа перехода различают:

переход типа short (короткий переход) — если адрес перехода находится в пределах от -127 до +128 байт от команды JMP;

переход типа near (ближний переход) — если адрес перехода находится в том же сегменте памяти, что и команда JMP;

переход типа far (дальний переход) — если адрес перехода находится в другом сегменте. Дальний переход может выполняться и в тот же самый сегмент, если в сегментной части операнда указано число, совпадающее с текущим значением CS;

переход с переключением задачи — передача управления другой задаче в многозадачной среде. Этот вариант будет рассмотрен в главе, посвященной защищенному режиму.

При выполнении переходов типа short и near команда JMP фактически изменяет значение регистра EIP (или IP), изменяя тем самым смещение следующей исполняемой команды относительно начала сегмента кода. Если операнд — регистр или переменная в памяти, то его значение просто копируется в EIP, как если бы это была команда MOV. Если операнд для JMP — непосредственно указанное число, то его значение суммируется с содержимым EIP, приводя к относительному переходу. В ассемблерных программах в качестве операнда обычно указывают имена меток, но на уровне исполнимого кода ассемблер вычисляет и записывает именно относительные смещения.

Выполняя дальний переход в реальном режиме, виртуальном режиме и в защищенном режиме (при переходе в сегмент с теми же привилегиями), команда JMP просто загружает новое значение в EIP и новый селектор сегмента кода в CS, используя старшие 16 бит операнда как новое значение для CS и младшие 16 или 32 — как значение IP или EIP.

Команда: Jcc метка
Назначение: Условный переход
Процессор: 8086
<
/p> Это набор команд, каждая из которых выполняет переход (типа short или near), если удовлетворяется соответствующее условие. Условием в каждом случае реально является состояние тех или иных флагов, но, если команда из набора Jcc используется сразу после СМР, условия приобретают формулировки, соответствующие отношениям между операндами СМР (см. табл. 7). Например, если операнды СМР были равны, то команда JE, выполненная сразу после этого СМР, осуществит переход. Операнд для всех команд из набора Jcc — 8-битное или 32-битное смешение относительно текущей команды.

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

Таблица 7. Варианты команды Jcc

Код команды Реальное условие Условие для CMP
JA
JNBE
CF = 0 и ZF = 0 если выше
если не ниже или равно
JAE
JNB
JNC
CF = 0 если выше или равно
если не ниже
если нет переноса
JB
JNAE
JC
CF = 1 если ниже
если не выше или равно
если перенос
JBE
JNA
CF = 1 и ZF = 1 если ниже или равно
если не выше
JE
JZ
ZF = 1 если равно
если ноль
JG
JNLE
ZF = 0 и SF = OF если больше
если не меньше или равно
JGE
JNL
SF = OF если больше или равно
если не меньше
JL
JNGE
SF <> OF если меньше
если не больше или равно
JLE
JNG
ZF = 1 и SF <> OF если меньше или равно
если не больше
JNE
JNZ
ZF = 0 если не равно
если не ноль
JNO OF = 0 если нет переполнения
JO OF = 1 если есть переполнение
JNP
JPO
PF = 0 если нет четности
если нечетное
JP
JPE
PF = 1 если есть четность
если четное
JNS SF = 0 если нет знака
JS SF = 1 если есть знак
Команды Jcc не поддерживают дальних переходов, так что, если требуется выполнить условный переход на дальнюю метку, необходимо использовать команду из набора Jcc с обратным условием и дальний JMP, как, например:

cmp ах,0 jne local_1 jmp far_label ; переход, если АХ = 0 lосаl_1:

Команда: JCXZ метка
Назначение: Переход, если СХ = 0
Процессор: 8086
Команда: JECXZ метка
Назначение: Переход, если EСХ = 0
Процессор: 80386
<


/p> Выполняет ближний переход на указанную метку, если регистр CX или ECX (для JCXZ и JECXZ соответственно) равен нулю. Так же как и команды из серии Jcc, JCXZ и JECXZ не могут выполнять дальних переходов. Проверка равенства СХ нулю, например, может потребоваться в начале цикла, организованного командой LOOPNE, — если в него войти с СХ = 0, то он будет выполнен 65 535 раз.

Команда: LOOP метка
Назначение: Цикл
Процессор: 8086
Уменьшает регистр ЕСХ на 1 и выполняет переход типа short на метку (которая не может быть дальше, чем на расстоянии от -128 до +127 байт от команды LOOP), если ЕСХ не равен нулю. Эта команда используется для организации циклов, в которых регистр ЕСХ (или СХ при 16-битной адресации) играет роль счетчика. Так, в следующем фрагменте команда ADD выполнится 10 раз:

mov cx,0Ah loop_start: add ax,cx loop loop_start

Команда LOOP полностью эквивалентна паре команд

dec ecx jz метка

Но LOOP короче этих двух команд на один байт и не изменяет значения флагов.

Команда: LOOPE метка
Назначение: Цикл, пока равно
Команда: LOOPZ метка
Назначение: Цикл, пока ноль
Команда: LOOPNE метка
Назначение: Цикл, пока не равно
Команда: LOOPNZ метка
Назначение: Цикл, пока не ноль
Процессор: 8086
Все эти команды уменьшают регистр ЕСХ на один, после чего выполняют переход типа short, если ЕСХ не равен нулю и если выполняется условие. Для команд LOOPE и LOOPZ условием является равенство единице флага ZF, для команд LOOPNE и LOOPNZ — равенство флага ZF нулю. Сами команды LOOPcc не изменяют значений флагов, так что ZF должен быть установлен (или сброшен) предшествующей командой. Например, следующий фрагмент копирует строку из DS:SI в строку в ES:DI (см. описание команд работы со строками), пока не кончится строка (СХ = 0) или пока не встретится символ с ASCII-кодом 13 (конец строки):

mov cx,str_length move_loop: stosb lodsb cmp al,13 loopnz move_loop

Команда: CALL операнд
Назначение: Вызов процедуры
Процессор: 8086
<


/p> Сохраняет текущий адрес в стеке и передает управление по адресу, указанному в операнде. Операндом может быть непосредственное значение адреса (метка в ассемблерных программах), регистр или переменная, содержащие адрес перехода. Если в качестве адреса перехода указано только смещение, считается, что адрес расположен в том же сегменте, что и команда CALL. При этом, так же как и в случае с JMP, выполняется ближний вызов процедуры. Процессор помещает значение регистра EIP (IP при 16-битной адресации), соответствующее следующей за CALL команде, в стек и загружает в EIP новое значение, осуществляя тем самым передачу управления. Если операнд CALL — регистр или переменная, то его значение рассматривается как абсолютное смещение, если операнд — метка в программе, то ассемблер указывает ее относительное смещение. Чтобы осуществить дальний CALL в реальном режиме, режиме V86 или в защищенном режиме при переходе в сегмент с теми же привилегиями, процессор помещает в стек значения регистров CS и EIP (IP при 16-битной адресации) и выполняет дальний переход аналогично команде JMP.

Команда: RET число
RETN число
RETF число
Назначение: Возврат из процедуры
Процессор: 8086
RETN считывает из стека слово (или двойное слово, в зависимости от режима адресации) и загружает его в IP (или EIP), выполняя тем самым действия, обратные ближнему вызову процедуры командой, CALL. RETF соответственно загружает из стека IP (EIP) и CS, возвращаясь из дальней процедуры. Если в ассемблерной программе указана команда RET, ассемблер заменит ее на RETN или RETF в зависимости от того, как была описана процедура, которую эта команда завершает. Операнд для RET необязателен, но, если он присутствует, после считывания адреса возврата из стека будет удалено указанное количество байт — это бывает нужно, если при вызове процедуры ей передавались параметры через стек.

Команда: INT число
Назначение: Вызов прерывания
Процессор: 8086
INT помещает в стек содержимое регистров EFLAGS, CS и EIP, после чего передает управление программе, называемой «обработчик прерывания» с указанным в качестве операнда номером (число от 0 до 0FFh), аналогично команде CALL. В реальном режиме адреса обработчиков прерываний считываются из таблицы, начинающейся в памяти по адресу 0000h:0000h. Адрес каждого обработчика занимает 4 байта, так что, например, адрес обработчика прерывания 10h находится в памяти по адресу 0000h:0040h. В защищенном режиме адреса обработчиков прерываний находятся в таблице IDT и обычно недоступны для прямого чтения или записи, так что для установки собственного обработчика программа должна обращаться к операционной системе. В DOS вызовы прерываний используются для выполнения большинства системных функций — работы с файлами, вводом/выводом и т.д. Например, следующий фрагмент кода завершает выполнение программы и возвращает управление DOS:

mov ax,4C01h int 21h



Команда: IRET
IRETD
Назначение: Возврат из обработчика прерывания
Процессор: 8086
Возврат управления из обработчика прерывания или исключения. IRЕТ загружает из стека значения IP, CS и FLAGS, a IRETD — EIP, CS и EFLAGS соответственно. Единственное отличие IRET от RETF состоит в том, что восстанавливается значение регистра флагов, из-за чего многим обработчикам прерываний приходится изменять значение EFLAGS, находящегося в стеке, чтобы, например, вернуть флаг CF установленным в случае ошибки.

Команда: INT3
Назначение: Вызов прерывания 3
Процессор: 8086
Размер этой команды — один байт (код 0CCh), что делает ее удобной для отладки программ отладчиками, работающими в реальном режиме. Такие отладчики записывают этот байт вместо первого байта команды, перед которой требуется точка останова, и переопределяют адрес обработчика прерывания 3 на соответствующую процедуру внутри отладчика.

Команда: INTO
Назначение: Вызов прерывания 4 при переполнении
Процессор: 8086
INTO — еще одна специальная форма команды INT. Эта команда вызывает обработчик прерывания 4, если флаг OF установлен в 1.

Команда: BOUND индекс, границы
Назначение: Проверка выхода за границы массива
Процессор: 80186
BOUND проверяет, не выходит ли значение первого операнда (регистр), взятое как число со знаком, за границы, указанные во втором операнде (переменная). Границы — два слова или двойных слова (в зависимости от разрядности операндов), рассматриваемые как целые со знаком, расположенные в памяти подряд. Первая граница считается нижней, вторая — верхней. Если индекс меньше нижней границы или больше верхней, вызывается прерывание 5 (или исключение #BR), причем адрес возврата указывает не на следующую команду, а на BOUND, так что обработчик должен исправить значение индекса или границ, прежде чем выполнять команду IRET.

Команда: ENTER размер, уровень
Назначение: Вход в процедуру
Процессор: 80186
<


/p> Команда ENTER создает стековый кадр заданного размера и уровня вложенности (оба операнда — числа; уровень вложенности может принимать значения только от 0 до 31) для вызова процедуры, использующей динамическое распределение памяти в стеке для своих локальных переменных. Так, команда

enter 2048,3

помещает в стек указатели на стековый кадр текущей процедуры и той, из которой вызывалась текущая, создает стековый кадр размером 2 килобайта для вызываемой процедуры и помещает в ЕВР адрес начала кадра. Пусть процедура MAIN имеет уровень вложенности 0, процедура PROCA запускается из MAIN и имеет уровень вложенности 1, и PROCB запускается из PROCA с уровнем вложенности 2. Тогда стек при входе в процедуру MAIN имеет вид, показанный на рис. 10.



Рис. 10. Стековый кадр процедуры 0-го уровня (MAIN)

Теперь процедура MAIN может определять свои локальные переменные в памяти, используя текущее значение ЕВР.

На первом уровне вложенности процедура PROCA, как показано на рис. 11, может создавать свои локальные переменные, используя текущее значение EBP, и получает доступ к локальным переменным процедуры MAIN, используя значение ЕВР для MAIN, помещенное в стек командой ENTER.



Рис. 11. Стековый кадр процедуры 1-го уровня (PROCA)

Процедура PROCB на втором уровне вложенности (рис. 12) получает доступ как к локальным переменным процедуры PROCA, используя значение ЕВР для PROCA, так и к локальным переменным процедуры МАIN, используя значение ЕВР для MAIN.



Рис. 12. Стековый кадр процедуры 2-го уровня (PROCB)

Команда: LEAVE
Назначение: Выход из процедуры
Процессор: 80186
Команда LEAVE выполняет действия, обратные команде ENTER. Фактически эта команда только копирует содержимое ЕВР в ESP, тем самым выбрасывая из стека весь кадр, созданный последней выполненной командой ENTER, и считывает из стека ЕВР для предыдущей процедуры, что одновременно восстанавливает и значение, которое имел ESP до вызова последней команды ENTER.


Команды пересылки данных FPU


Команда: FLD источник
Назначение: Загрузить вещественное число в стек
Процессор: 8087

Команда помещает содержимое источника (32-, 64- или 80-битная переменная или ST(n)) и уменьшает ТОР на 1. Команда FLD ST(0) делает копию вершины стека.

Команда: FST приемник
Назначение: Скопировать вещественное число из стека
Команда: FSTP приемник
Назначение: Считать вещественное число из стека
Процессор: 8087

Копирует ST(0) в приемник (32- или 64-битную переменную или пустой ST(n) в случае FST; 32-, 64- или 80-битную переменную или пустой ST(n) в случае FSTP). FSTP после этого выталкивает число из стека (помечает ST(0) как пустой и увеличивает ТОР на один).

Команда: FILD источник
Назначение: Загрузить целое число в стек
Процессор: 8087

Преобразовывает целое число со знаком из источника (16-, 32- или 64-битная переменная) в вещественный формат, помещает в вершину стека и уменьшает ТОР на 1.

Команда: FIST приемник
Назначение: Скопировать целое число из стека
Команда: FISTP приемник
Назначение: Считать целое число из стека
Процессор: 8087

Преобразовывает число из вершины стека в целое со знаком и записывает его в приемник (16- или 32-битная переменная для FIST; 16-, 32- или 64-битная переменная для FISTP). FISTP после этого выталкивает число из стека (помечает ST(0) как пустой и увеличивает ТОР на один). Попытка записи слишком большого числа, бесконечности или не-числа приводит к исключению «недопустимая операция» (и записи целой неопределенности, если IM = 1).

Команда: FBLD источник
Назначение: Загрузить десятичное число в стек
Процессор: 8087

Преобразовывает BCD число из источника (80-битная переменная в памяти), помещает в вершину стека и уменьшает ТОР на 1.

Команда: FBSTP приемник
Назначение: Считать десятичное число из стека
Процессор: 8087

Преобразовывает число из вершины стека в 80-битное упакованное десятичное, записывает его в приемник (80-битная переменная) и выталкивает это число из стека (помечает ST(0) как пустой и увеличивает ТОР на один). Попытка записи слишком большого числа, бесконечности или не-числа приводит к исключению «недопустимая операция» (и записи десятичной неопределенности, если IM = 1).

Команда: FXCH приемник
Назначение: Обменять местами два регистра стека
Процессор: 8087
<
/p> Обмен местами содержимого регистра ST(0) и источника (регистр ST(n)). Если операнд не указан, обменивается содержимое ST(0) и ST(1).

Команда: FCMOVcc приемник, источник
Назначение: Условная пересылка данных
Процессор: P6
Это набор команд, каждая из которых копирует содержимое источника (регистр ST(n)) в приемник (только ST(0)), если выполняется соответствующее условие. Реально каждое условие соответствует тем или ным значениям флагов регистра FLAGS, но после команд

fcom (или другие команды сравнения) fstsw ax sahf

в регистр FLAGS загружаются флаги С0, С1 и С3, и последующая команда из набора FCMOVcc приобретает смысл обработки результата предыдущего сравнения (табл. 13).

Таблица 13. Команды FCMOVcc

Команда Значения флагов Действие после FCOM
FCMOVE ZF = 1 если равно
FCMOVNE ZF = 0 если не равно
FCMOVB CF = 1 если меньше
FCMOVBE CF = 1 и ZF = 1 если меньше или равно
FCMOVNB CF = 0 если не меньше
FCMOVNBE CF = 0 и ZF = 0 если не меньше или равно
FCMOVU PF = 1 если несравнимы
FCMOVNU PF = 0 если сравнимы

Команды пересылки данных ММХ


Команда: MOVD приемник,источник
Назначение: Пересылка двойных слов
Процессор: ММХ

Команда копирует двойное слово из источника (регистр ММХ, обычный регистр или переменная) в приемник (регистр ММХ, обычный регистр или переменная, но хотя бы один из операндов обязательно должен быть регистром ММХ). Если приемник — регистр ММХ, двойное слово записывается в его младшую половину (биты 31 – 0), а старшая заполняется нулями. Если источник — регистр ММХ, в приемник записывается младшее двойное слово этого регистра.

Команда: MOVQ приемник,источник
Назначение: Пересылка учетверенных слов
Процессор: ММХ

Копирует учетверенное слово (64 бита) из источника (регистр ММХ или переменная) в приемник (регистр ММХ или переменная, оба операнда не могут быть переменными).



Команды преобразования типов ММХ


Команда: PACKSSWB приемник,источник
PACKSSDW приемник,источник
Назначение: Упаковка со знаковым насыщением
Процессор: ММХ

Команды упаковывают и насыщает слова со знаком в байты (PACKSSWB) или двойные слова со знаком в слова (PACKSSDW). Команда PACKSSWB копирует четыре слова (со знаком), находящиеся в приемнике (регистр ММХ), в 4 младших байта (со знаком) приемника и копирует четыре слова (со знаком) из источника (регистр ММХ или переменная) в старшие четыре байта (со знаком) приемника. Если значение какого-нибудь слова больше +127 (7Fh) или меньше -128 (80h), в байты помещаются числа +127 и -128 соответственно. Команда PACKSSDW аналогично копирует два двойных слова из приемника в два младших слова приемника и два двойных слова из источника в два старших слова приемника. Если значение какого-нибудь двойного слова больше +32 767 (7FFFh) или меньше -32 768 (8000h), в слова помещаются числа +32 767 и -32 768 соответственно.

Команда: PACKUSWB приемник,источник
Назначение: Упаковка с беззнаковым насыщением
Процессор: ММХ

Копирует четыре слова (со знаком), находящиеся в приемнике (регистр ММХ), в 4 младших байта (без знака) приемника и копирует четыре слова (со знаком) из источника (регистр ММХ или переменная) в старшие четыре байта (без знака) приемника. Если значение какого-нибудь слова больше 255 (FFh) или меньше 0 (00h), в байты помещаются числа 255 и 0 соответственно.

Команда: PUNPCKHBW приемник,источник
PUNPCKHWD приемник,источник
PUNPCKHDQ приемник,источник
Назначение: Распаковка и объединение старших элементов
Процессор: ММХ

Команды распаковывают старшие элементы источника (регистр ММХ или переменная) и приемника (регистр ММХ) и записывают их в приемник через один (см. рис. 15).

Команда PUNPCKHBW объединяет по 4 старших байта источника и приемника, команда PUNPCKHWD объединяет по 2 старших слова, и команда PUNPCKHDQ копирует в приемник по одному старшему двойному слову из источника и приемника.

Если источник содержит нули, эти команды фактически переводят старшую половину приемника из одного формата данных в другой, дополняя увеличиваемые элементы нулями. PUNPCKHBW переводит упакованные байты в упакованные слова, PUNPCKHWD переводит слова в двойные слова, и PUNPCKHDQ переводит единственное старшее двойное слово приемника в учетверенное.



Рис. 15. Действие команды PUNPCKHBW

Команда: PUNPCKLBW приемник,источник
PUNPCKLWD приемник,источник
PUNPCKLDQ приемник,источник
Назначение: Распаковка и объединение младших элементов
Процессор: ММХ
Команды распаковывают младшие элементы источника (регистр ММХ или переменная) и приемника (регистр ММХ) и записывают их в приемник через один аналогично предыдущим командам. Команда PUNPCKLBW объединяет по 4 младших байта источника и приемника, команда PUNPCKLWD объединяет по 2 младших слова, и команда PUNPCKLDQ копирует в приемник по одному младшему двойному слову из источника и приемника. Если источник содержит только нули, эти команды, аналогично PUNPCKH*, фактически переводят младшую половину приемника из одного формата данных в другой, дополняя увеличиваемые элементы нулями.


Команды сравнения FPU


Команда: FCOM источник
Назначение: Сравнить вещественные числа
Команда: FCOMP источник
Назначение: Сравнить и вытолкнуть из стека
Команда: FCOMPP источник
Назначение: Сравнить и вытолкнуть из стека два числа
Процессор: 8087

Команды выполняют сравнение содержимого регистра ST(0) с источником (32- или 64-битная переменная или регистр ST(n), если операнд не указан — ST(1)) и устанавливают флаги С0, С2 и С3 в соответствии с таблицей 14.

Таблица 14. Флаги сравнения FPU

Условие C3 C2 C0
ST(0) > источник 0 0 0
ST(0) < источник 0 0 1
ST(0) = источник 1 0 0
Не сравнимы 1 1 1

Если один из операндов — не-число или неподдерживаемое число, происходит исключение «недопустимая операция», а если оно замаскировано (флаг IM = 1), все три флага устанавливаются в 1. После команд сравнения с помощью команд FSTSW и SAHF можно перевести флаги С3, С2 и С0 в соответственно ZF, PF и CF, после чего все условные команды (Jcc, CMOVcc, FCMOVcc, SETcc) могут использовать результат сравнения, как после команды СМР.

Команда FCOMP после выполнения сравнения выталкивает из стека содержимое ST(0) (помечает его как пустой и увеличивает ТОР на 1), а команда FCOMPP выталкивает из стека и ST(0), и ST(1).

Команда: FUCOM источник
Назначение: Сравнить вещественные числа без учета порядков
Команда: FUCOMP источник
Назначение: Сравнить без учета порядков и вытолкнуть из стека
Команда: FUCOMPP источник
Назначение: Сравнить без учета порядков и вытолкнуть из стека два числа
Процессор: 80387

Эти команды аналогичны FCOM/FCOMP/FCOMPP во всем, кроме того, что в роли источника могут выступать только регистры ST(n), и если один из операндов — QNAN («тихое» не-число), флаги С3, С2, С0 устанавливаются в единицы, но исключение «недопустимая операция» не вызывается. Если один из операндов — SNAN или неподдерживаемое число, эти команды ведут себя так же, как и обычное сравнение.

Команда: FICOM источник
Назначение: Сравнить целые числа
Команда: FICOMP источник
Назначение: Сравнить целые и вытолкнуть из стека
Процессор: 8087
<
/p> Эти команды сравнивают содержимое регистра ST(0) и источника (16- или 32-битная переменная), причем считается, что источник содержит целое число. В остальном действие FICOM/FICOMP полностью эквивалентно FCOM/FCOMP.

Команда: FCOMI источник
Назначение: Сравнить и установить EFLAGS
Команда: FCOMIP источник
Назначение: Сравнить, установить EFLAGS и вытолкнуть
Команда: FUCOMI источник
Назначение: Сравнить без учета порядков и установить EFLAGS
Команда: FUCOMIP источник
Назначение: Сравнить без учета порядков, установить EFLAGS и вытолкнуть из стека
Процессор: P6
Выполняет сравнение регистра ST(0) и источника (регистр ST(n)) и устанавливает флаги регистра EFLAGS соответственно таблице 15.

Таблица 15. Флаги после соманд FCOMI

Условие ZF PF CF
ST(0) > источник 0 0 0
ST(0) < источник 0 0 1
ST(0) = источник 1 0 0
Не сравнимы 1 1 1
Эти команды эквивалентны командам FCOM/FCOMP/FUCOM/FUCOMP, вслед за которыми исполняются FSMSW АХ и SAHF, но они не изменяют содержимого регистра АХ и выполняются быстрее.

Команда: FTST
Назначение: Проверить, не содержит ли SP(0) ноль
Процессор: 8087
Сравнивает содержимое ST(0) с нулем и выставляет флаги С3, С2 и С0 аналогично другим командам сравнения.

Команда: FXAM
Назначение: Проанализировать содержимое ST(0)
Процессор: 8087
Устанавливает флаги С3, С2 и С0 в зависимости от типа числа, находящегося в ST(0), в соответствии с правилами, приведенными в таблице 16.

Флаг С1 устанавливается равным знаку числа в ST(0) независимо от типа числа (на самом деле он устанавливается, даже если регистр помечен как пустой).

Таблица 16. Результаты действия команды FXAM

Тип числа C3 C2 C0
Неподдерживаемое 0 0 0
Не-число 0 0 1
Нормальное конечное число 0 1 0
Бесконечность 0 1 1
Ноль 1 0 0
Регистр пуст 1 0 1
Денормализованное число 1 1 0

Команды сравнения ММХ


Команда: PCMPEQB приемник,источник
PCMPEQW приемник,источник
PCMPEQD приемник,источник
Назначение: Проверка на равенство
Процессор: ММХ

Команды сравнивают индивидуальные элементы данных (байты — в случае PCMPEQB, слова — в случае PCMPEQW, двойные слова — в случае PCMPEQD) источника (регистр ММХ или переменная) с соответствующими элементами приемника (регистр ММХ). Если пара сравниваемых элементов равна, соответствующий элемент приемника заполняется единицами, если они не равны — элемент заполняется нулями.

Команда: PCMPGTB приемник,источник
PCMPGTW приемник,источник
PCMPGTD приемник,источник
Назначение: Сравнение
Процессор: ММХ

Команды сравнивают индивидуальные элементы данных (байты — в случае PCMPGTB, слова — в случае PCMPGTW, двойные слова — в случае PCMPGTD) источника (регистр ММХ или переменная) с соответствующими элементами приемника (регистр ММХ). Если элемент приемника больше, чем соответствующий элемент источника, все биты в этом элементе приемника устанавливаются в единицы. Если элемент приемника меньше или равен элементу источника, он обнуляется.



Команды управления FPU


Команда: FINCSTP
Назначение: Увеличить указатель вершины стека
Процессор: 8087

Поле ТОР регистра состояния FPU увеличивается на 1. Если ТОР было равно семи, оно обнуляется. Эта команда не эквивалентна выталкиванию ST(0) из стека, потому что регистр данных, который назывался ST(0) и стал ST(7), не помечается как пустой.

Команда: FDECSTP
Назначение: Уменьшить указатель вершины стека
Процессор: 8087

Поле ТОР регистра состояния FPU уменьшается на 1. Если ТОР было равно нулю, оно устанавливается в 7. Содержимое регистров данных и TW не изменяется.

Команда: FFREE операнд
Назначение: Освободить регистр данных
Процессор: 8087

Команда отмечает в регистре TW, что операнд (регистр данных ST(n)) — пустой. Содержимое регистра и ТОР не изменяются.

Команда: FINIT
Назначение: Инициализировать FPU
Команда: FNINIT
Назначение: Инициализировать FPU без ожидания
Процессор: 8087

Команды FINIT и FNINIT восстанавливают значения по умолчанию в регистрах CR, SR, TW, а начиная с 80387 — FIP и FDP Управляющий регистр инициализируется значением 037Fh (округление к ближайшему, 64-битная точность, все исключения замаскированы). Регистр состояния обнуляется (ТОР = 0, никакие флаги исключений не установлены). Регистры данных никак не изменяются, но все они помечаются пустыми в регистре TW. Регистры FIP и FDP обнуляются. Команда FINIT, в отличие от FNINIT, проверяет наличие произошедших и необработанных исключений и обрабатывает их до инициализации. Команда FINIT полностью эквивалентна (и на самом деле является) WAIT FNINIT.

Команда: FCLEX
Назначение: Обнулить флаги исключений
Команда: FNCLEX
Назначение: Обнулить флаги исключений без ожидания
Процессор: 8087

Команды обнуляют флаги исключений (РЕ, UE, OF, ZE, DE, IE), а также флаги ES, SF и В в регистре состояния FPU. Команда FCLEX, в отличие от FNCLEX, проверяет наличие произошедших и необработанных исключений и обрабатывает их до выполнения. Команда FCLEX полностью эквивалентна (и на самом деле является) WAIT FNCLEX.

Команда: FSTCW приемник
Назначение: Сохранить регистр CR
Команда: FNSTCW приемник
Назначение: Сохранить регистр CR без ожидания
Процессор: 8087
<
/p> Команды копируют содержимое CR в приемник (16-битная переменяя). Команда FSTCW, в отличие от FNSTCW, проверяет наличие произошедших и необработанных исключений и обрабатывает их до выполнения. Команда FSTCW полностью эквивалентна (и на самом деле является) WAIT FNSTCW.

Команда: FLDCW источник
Назначение: Загрузить регистр CR
Процессор: 8087
Копирует содержимое источника (16-битная переменная) в регистр CR. Если один или несколько флагов исключений установлены в регистре SR и замаскированы в CR, а команда FLDCW эти маски удалила, исключения будут обработаны перед началом выполнения следующей команды FPU (кроме команд без ожидания). Чтобы этого не происходило, обычно перед FLDCW выполняют команду FCLEX.

Команда: FSTENV приемник
Назначение: Сохранить вспомогательные регистры
Команда: FNSTENV приемник
Назначение: Сохранить вспомогательные регистры без ожидания
Процессор: 8087
Сохраняет все вспомогательные регистры FPU в приемник (14 или 28 байт в памяти, в зависимости от разрядности операндов) и маскирует все исключения. Сохраняет содержимое регистров CR, SR, TW, FIP, FDP и последнюю команду в формате, зависящем от текущей разрядности операндов и адресов (7 двойных слов для 32-битных операндов и 7 слов для 16-битных операндов). Первое слово (или младшая половина первого двойного слова в 32-битном случае) всегда содержит CR, второе слово — SR, третье слово — TW, четвертое — FIP. Использование последних трех слов варьируется в зависимости от текущей разрядности адресации и операндов.

32-битные операнды и 32-битная адресация:

Двойное слово 5: биты 10 – 0 старшего слова — код последней команды, младшее слово — селектор для FIP.

Двойное слово 6: FDP (32-битный).

Двойное слово 7: младшее слово содержит селектор для FDP.

32-битные операнды и 16-битная адресация:

Двойное слово 5: биты 31 – 16 — FIP, биты 10 – 0 — код последней команды.



Двойное слово 6: биты 15 – 0 — FDP.

Двойное слово 7: биты 31 – 16 — FDP.

16-битные операнды и 32-битная адресация:

Слово 5: селектор для FIP.

Слово 6: FDP.

Слово 7: селектор для FDP.

16-битные операнды и 16-битная адресация:

Слово 5: биты 15 – 12 — биты 19 – 16 20-битного FIP, биты 10 – 0 — код последней команды.

Слово 6: FDP.

Слово 7: биты 15 – 12 — биты 19 – 16 20-битного FDP.

Из кода последней выполненной FPU- команды сохраняются первые два байта без префиксов и без первых пяти бит, которые одинаковы для всех команд FPU, то есть всего 11 бит. Команда FSTENV, в отличие от FNSTENV, проверяет наличие произошедших и необработанных исключений и обрабатывает их до выполнения. Команда FSTENV полностью эквивалентна (и на самом деле является) WAIT FNSTENV.

Команда: FLDENV источник
Назначение: Загрузить вспомогательные регистры
Процессор: 8087
Команда загружает все вспомогательные регистры FPU (регистры CR, SR, TW, FIP, FDP) из источника (область памяти в 14 или 28 байт, в зависимости от разрядности операндов), сохраненные ранее командой FSTENV/FNSTENV. Если в загружаемом SW установлены несколько (или один) флагов исключений, которые одновременно не замаскированы флагами CR, эти исключения будут выполнены перед следующей командой FPU (кроме команд без ожидания).

Команда: FSAVE приемник
Назначение: Сохранить состояние FPU
Команда: FNSAVE приемник
Назначение: Сохранить состояние FPU без ожидания
Процессор: 8087
Сохраняет состояние FPU (регистры данных и вспомогательные регистры) в приемник (область памяти размером 94 или 108 байт, в зависимости от разрядности операндов) и инициализирует FPU аналогично командам FINIT/FNINIT. Команда FSAVE, в отличие от FNSAVE, проверяет наличие произошедших и необработанных исключений и обрабатывает их до выполнения. Команда FSAVE полностью эквивалентна (и на самом деле является) WAIT FNSAVE. Эта команда обычно используется операционной системой при переключении задач или программами, которые должны передавать вызываемым процедурам чистый FPU.

Команда: FXSAVE приемник
Назначение: Быстрое сохранение состояния FPU
Процессор: PII
<


/p> Команда FXSAVE сохраняет все текущее состояние FPU, включая все регистры, в приемник (512-байтную область памяти с адресом, кратным 16), не проверяя на необработанные исключения, аналогично команде FNSAVE. Кроме того, в отличие от FSAVE/FNSAVE, эта команда не переинициализирует FPU после сохранения состояния. Эта команда несовместима с FSAVE/FRSTOR.

Команда: FRSTOR источник
Назначение: Восстановить состояние FPU
Процессор: 8087
Загружает состояние FPU (вспомогательные регистры и регистры данных) из источника (область в памяти размером в 94 или 108 байт, в зависимости от разрядности операндов).

Команда: FXRSTOR источник
Назначение: Быстрое восстановление состояния FPU
Процессор: PII
Команда FXRSTOR восстанавливает все текущее состояние FPU, включая все регистры, из источника (512-байтной области памяти с адресом, кратным 16), который был заполнен командой FXSAVE.

Команда: FSTSW приемник
Назначение: Сохранить регистр SR
Команда: FNSTSW приемник
Назначение: Сохранить регистр SR без ожидания
Процессор: 80287
Сохраняет текущее значение регистра SR в приемник (регистр АХ или 16-битная переменная). Команда FSTSW АХ обычно используется после команд сравнения и FPREM/FPREM1/FXAM, чтобы выполнять условные переходы.

Команда: WAIT
FWAIT
Назначение: Ожидание готовности FPU
Процессор: 8087
Процессор проверяет, присутствуют ли необработанные и немаскированные исключения FPU, и обрабатывает их. Эту команду можно указывать в критических ситуациях после команд FPU, чтобы убедиться, что возможные исключения будут обработаны. WAIT и FWAIT — разные названия для одной и той же команды.

Команда: FNOP
Назначение: Отсутствие операции
Процессор: 8087
Эта команда занимает место и время, но не выполняет никакого действия. Устаревшие команды FPU — FENI (разрешить исключения, 8087), FDISI (запретить исключения, 8087) и FSETPM (80287) выполняются, как FNOP, всеми более старшими процессорами.


Команды управления состоянием ММХ


Команда: EMMS
Назначение: Освободить регистры ММХ
Процессор: ММХ

Если выполнялись какие-нибудь команды ММХ (кроме EMMS), все регистры FPU помечаются как занятые (в регистре TW). Команда EMMS помечает все регистры FPU как пустые для того, чтобы после завершения работы с ММХ можно было передать управление процедуре, использующей FPU.



Константы FPU


Команда: FLD1
Назначение: Поместить в стек 1,0
Команда: FLDZ
Назначение: Поместить в стек +0,0
Команда: FLDPI
Назначение: Поместить в стек число
Команда: FLDL2E
Назначение: Поместить в стек log2(e)
Команда: FLDL2T
Назначение: Поместить в стек log2(10)
Команда: FLDLN2
Назначение: Поместить в стек ln(2)
Команда: FLDLG2
Назначение: Поместить в стек lg(2)
Процессор: 8087

Все эти команды помещают в стек (то есть уменьшают ТОР на один и помещают в ST(0)) соответствующую часто используемую константу. Начиная с сопроцессора 80387, все константы хранятся в более точном формате, чем 80-битный формат, используемый в регистрах данных, и при загрузке в стек происходит округление в соответствии с полем RC.



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


По аналогии с регистровыми и непосредственными операндами адрес операнда в памяти также можно не указывать непосредственно, а хранить в любом регистре. До 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 ax,[esi*2]+2

Множитель, который может быть равен 1, 2, 4 или 8, соответствует размеру элемента массива — байту, слову, двойному слову, учетверенному слову соответственно. Из регистров в этом варианте адресации можно использовать только EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP, но не SI, DI, BP или SP, которые можно было использовать в предыдущих вариантах.



Логические операции


Команда: AND приемник, источник
Назначение: Логическое И
Процессор: 8086

Команда выполняет побитовое «логическое И» над приемником (регистр или переменная) и источником (число, регистр или переменная; источник и приемник не могут быть переменными одновременно) и помещает результат в приемник. Любой бит результата равен 1, только если соответствующие биты обоих операндов были равны 1, и равен 0 в остальных случаях. Наиболее часто AND применяют для выборочного обнуления отдельных бит, например, команда

and al,00001111b

обнулит старшие четыре бита регистра AL, сохранив неизменными четыре младших.

Флаги OF и CF обнуляются, SF, ZF и PF устанавливаются в соответствии с результатом, AF не определен.

Команда: OR приемник, источник
Назначение: Логическое ИЛИ
Процессор: 8086

Выполняет побитовое «логическое ИЛИ» над приемником (регистр или переменная) и источником (число, регистр или переменная; источник и приемник не могут быть переменными одновременно) и помещает результат в приемник. Любой бит результата равен 0, только если соответствующие биты обоих операндов были равны 0, и равен 1 в остальных случаях. Команду OR чаще всего используют для выборочной установки отдельных бит, например, команда

or al,00001111b

приведет к тому, что младшие четыре бита регистра AL будут установлены в 1.

При выполнении команды OR флаги OF и CF обнуляются, SF, ZF и PF устанавливаются в соответствии с результатом, AF не определен.

Команда: XOR приемник, источник
Назначение: Логическое исключающее ИЛИ
Процессор: 8086

Выполняет побитовое «логическое исключающее ИЛИ» над приемником (регистр или переменная) и источником (число, регистр или переменная; источник и приемник не могут быть переменными одновременно) и помещает результат в приемник. Любой бит результата равен 1, если соответствующие биты операндов различны, и нулю, если одинаковы. XOR используется для самых разных операций, например:

xor ах,ах ; обнуление регистра АХ


или

xor ах,bх xor bх,ах xor ах,bх ; меняет местами содержимое АХ и ВХ

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

mov ax,0

или

xchg ax,bx

Команда: NOT приемник
Назначение: Инверсия
Процессор: 8086
Каждый бит приемника (регистр или переменная), равный нулю, устанавливается в 1, и каждый бит, равный 1, сбрасывается в 0. Флаги не затрагиваются.

Команда: TEST приемник, источник
Назначение: Логическое сравнение
Процессор: 8086
Вычисляет результат действия побитового «логического И» над приемником (регистр или переменная) и источником (число, регистр или переменная; источник и приемник не могут быть переменными одновременно) и устанавливает флаги SF, ZF и PF в соответствии с полученным результатом, не сохраняя результат (флаги OF и CF обнуляются, значение AF не определено). TEST, так же как и СМР, используется в основном в сочетании с командами условного перехода (Jcc), условной пересылки данных (CMOVcc) и условной установки байт (SETcc).


Логические операции ММХ


Команда: PAND приемник,источник
Назначение: Логическое И
Процессор: ММХ

Команда выполняет побитовое «логическое И» над источником (регистр ММХ или переменная) и приемником (регистр ММХ) и сохраняет результат в приемнике. Каждый бит результата устанавливается в 1, если соответствующие биты в обоих операндах равны 1, иначе бит сбрасывается в 0.

Команда: PANDN приемник,источник
Назначение: Логическое НЕ-И (штрих Шеффера)
Процессор: ММХ

Выполняет побитовое «логическое НЕ» (то есть инверсию бит) над приемником (регистр ММХ) и затем побитовое «логическое И» над приемником и источником (регистр ММХ или переменная). Результат сохраняется в приемнике. Каждый бит результата устанавливается в 1, только если соответствующий бит источника был равен 1, а приемника — 0, иначе бит сбрасывается в 0. Эта логическая операция носит также название «штрих Шеффера».

Команда: POR приемник,источник
Назначение: Логическое ИЛИ
Процессор: ММХ

Выполняет побитовое «логическое ИЛИ» над источником (регистр ММХ или переменная) и приемником (регистр ММХ) и сохраняет результат в приемнике. Каждый бит результата сбрасывается в 0, если соответствующие биты в обоих операндах равны 0, иначе бит устанавливается в 1.

Команда: PXOR приемник,источник
Назначение: Логическое исключающее ИЛИ
Процессор: ММХ

Выполняет побитовое «логическое исключающее ИЛИ» над источником (регистр ММХ или переменная) и приемником, (регистр ММХ) и сохраняет результат в приемнике. Каждый бит результата устанавливается в 1, если соответствующие биты в обоих операндах равны, иначе бит сбрасывается в 0.



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


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

mov ax,2

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



Операции над битами и байтами


Команда: BT база, смещение
Назначение: Проверка бита
Процессор: 80386

Команда ВТ считывает во флаг CF значение бита из битовой строки, указанной первым операндом, битовой базой (регистр или переменная), со смещением, указанным во втором операнде, битовом смещении (число или регистр). Если первый операнд — регистр, то битовой базой считается бит 0 в указанном регистре и смещение не может превышать 15 или 31 (в зависимости от размера регистра); если оно превышает эти границы, в качестве смещения будет использован остаток от деления его на 16 или 32 соответственно. Если первый операнд — переменная, то в качестве битовой базы используется бит 0 указанного байта в памяти, а смещение может принимать значения от 0 до 31, если оно указано непосредственно (старшие биты процессором игнорируются), и от -231 до 231–1, если оно указано в регистре.

Несмотря на то что эта команда считывает единственный бит из памяти, процессор считывает целое двойное слово по адресу База+(4*(Смещение/32)) или слово по адресу База+(2*(Смещение/16)), в зависимости от разрядности адреса, так что не следует пользоваться ВТ вблизи от не доступных для чтения областей памяти.

После выполнения команды ВТ флаг CF равен значению считанного бита, флаги OF, SF, ZF, AF и PF не определены.

Команда: BTS база, смещение
Назначение: Проверка и установка бита
Команда: BTR база, смещение
Назначение: Проверка и сброс бита
Команда: BTC база, смещение
Назначение: Проверка и инверсия бита
Процессор: 80386

Эти три команды соответственно устанавливают в 1 (BTS), сбрасывают в 0 (ВТR) и инвертируют (ВТС) значение бита, который находится в битовой строке с началом, указанным в базе (регистр или переменная), и смещением, указанным во втором операнде (число от 0 до 31 или регистр). Если битовая база — регистр, то смещение не может превышать 15 или 31 в зависимости от разрядности этого регистра. Если битовая база — переменная в памяти, то смещение может принимать значения от -231 до 231–1 (если оно указано в регистре).


После выполнения команд BTS, BTR и ВТС флаг CF равен значению считанного бита до его изменения в результате действия команды, флаги OF, SF, ZF, AF и PF не определены.

Команда: BSF приемник, источник
Назначение: Прямой поиск бита
Команда: BSR база, смещение
Назначение: Обратный поиск бита
Процессор: 80386
BSF сканирует источник (регистр или переменная), начиная с самого младшего бита, и записывает в приемник (регистр) номер первого встретившегося бита, равного 1. Команда BSR сканирует источник, начиная с самого старшего бита, и возвращает номер первого встретившегося ненулевого бита, считая от нуля, то есть, если источник равен 0100 0000 0000 0010b, то BSF возвратит 1 a BSR — 14.

Если весь источник равен нулю, значение приемника не определено и флаг ZF устанавливается в 1, иначе ZF всегда сбрасывается. Флаги CF, OF, SF, AF и PF не определены.

Команда: SETcc приемник
Назначение: Установка байта по условию
Процессор: 80386
Это набор команд, которые устанавливают приемник (восьмибитный регистр или переменная размером в один байт) в 1 или 0, если удовлетворяется или не удовлетворяется определенное условие. Условием в каждом случае реально является состояние тех или иных флагов, но, если команда из набора SETcc используется сразу после СМР, условия приобретают формулировки, соответствующие отношениям между операндами СМР (см. табл. 6). Скажем, если операнды СМР были неравны, то команда SETNE, выполненная сразу после этого СМР, установит значение своего операнда в 1.

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

Таблица 6. Команды SETcc

Код команды Реальное условие Условие для CMP
SETA
SETNBE
CF = 0 и ZF = 0 если выше
если не ниже или равно
SETAE
SETNB
SETNC
CF = 0 если выше или равно
если не ниже
если нет переноса
SETB
SETNAE
SETC
CF = 1 если ниже
если не выше или равно
если перенос
SETBE
SETNA
CF = 1 и ZF = 1 если ниже или равно
если не выше
SETE
SETZ
ZF = 1 если равно
если ноль
SETG
SETNLE
ZF = 0 и SF = OF если больше
если не меньше или равно
SETGE
SETNL
SF = OF если больше или равно
если не меньше
SETL
SETNGE
SF <> OF если меньше
если не больше или равно
SETLE
SETNG
ZF = 1 и SF <> OF если меньше или равно
если не больше
SETNE
SETNZ
ZF = 0 если не равно
если не ноль
SETNO OF = 0 если нет переполнения
SETO OF = 1 если есть переполнение
SETNP
SETPO
PF = 0 если нет четности
если нечетное
SETP
SETPE
PF = 1 если есть четность
если четное
SETNS SF = 0 если нет знака
SETS SF = 1 если есть знак

Основные непривилегированные команды


В этой главе описаны все непривилегированные команды процессоров Intel серии х86, включая команды расширений IA NPX (чаще называемый FPU — расширение для работы с числами с плавающей запятой) и IA MMX (мультимедийное расширение). Для каждой команды указана форма записи, название и модель процессоров Intel, начиная с которой она поддерживается: 8086, 80186, 80286, 80386, 80486, Р5 (Pentium), MMX, P6 (Pentium Pro и Pentium II).



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


Команда: 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
<


/p> 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
<


/p> Копирует содержимое источника (регистр или переменная размером в байт или слово) в приемник (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, и не изменяют флаги)

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


Если известен адрес операнда, располагающегося в памяти, можно использовать этот адрес. Если операнд — слово, находящееся в сегменте, на который указывает 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 и в других ситуациях, когда программа будет запускаться в защищенном режиме, смещение не может превышать границ двойного слова.



Процессоры Intel в реальном режиме


Процессор Intel x86 после включения питания оказывается в так называемом режиме реальной адресации памяти, или просто реальном режиме. Большинство операционных систем сразу же переводят его в защищенный режим, позволяющий им обеспечивать многозадачность, распределение памяти и другие функции. Пользовательские программы в таких операционных системах часто работают еще в одном режиме, режиме V86, из которого им доступно все то же, что и из реального, кроме команд, относящихся к управлению защищенным режимом. Таким образом, эта глава описывает не только реальный режим, но и V86, то есть все то, что доступно программисту, если он не проектирует операционную систему или DPMI-сервер, в подавляющем большинстве случаев.



Расширение AMD 3D


Процессоры AMD, начиная с AMD К6 3D, поддерживают дополнительное расширение набора команд ММХ. В AMD 3D вводится новый тип данных — упакованные 32-битные вещественные числа, определяются новые команды (начинающиеся с PF) и несколько дополнительных команд для работы с обычными ММХ-типами данных:

PI2FD приемник, источник — преобразовывает упакованные 32-битные целые со знаком (двойные слова) в упакованные вещественные числа;

PF2ID приемник, источник — преобразовывает упакованные вещественные в упакованные целые числа со знаком (преобразование с насыщением);

PAVGUSB приемник, источник — вычисляет средние арифметические для упакованных 8-битных целых чисел без знака;

PMULHRW приемник, источник — перемножает упакованные 16-битные целые со знаком и сохраняет результаты как 16-битные целые в приемнике (при переполнениях выполняется насыщение);

PFACC приемник, источник — сумма вещественных чисел в приемнике помещается в младшую половину приемника, сумма вещественных чисел из источника помещается в старшую половину приемника;

PFADD приемник, источник — сложение упакованных вещественных чисел;

PFSUB приемник, источник — вычитание упакованных вещественных чисел;

PFSUBR приемник, источник — обратное вычитание (приемник из источника) упакованных вещественных чисел;

PFMUL приемник,источник — умножение упакованных вещественных чисел.

Набор команд для быстрого вычисления по итерационным формулам:

Быстрое деление:

xi+1 = хi(2 - bхi)

х0 = PFRCP(b)

х1 = PFRCPIT1(b, x0)

х2 = PFRCPIT2(x1, x0)

х4 = PFMUL(b, x3)

Быстрое вычисление квадратного корня:

хi+1 = хi(3 - bxi2)/2

х0 = PFRSQRT(b)

х1 = PFMUL(x0, x0)

х2 = PFRSQIT(b, x1)

х3 = PFRCPIT2(x2, x0)

х4 = PFMUL(b, x3)

PFCMPEQ приемник, источник — проверка равенства для упакованных вещественных чисел (полностью аналогично PCMPEQW);


PFCMPGE приемник, источник — сравнение упакованных вещественных чисел: если число в приемнике больше или равно числу в источнике, все его биты устанавливаются в 1;

PFCMPGT приемник, источник — сравнение упакованных вещественных чисел: если число в приемнике больше числа в источнике, все его биты устанавливаются в 1;

PFMAX приемник, источник — сохраняет в приемнике максимальное из каждой пары сравниваемых вещественных чисел;

PFMIN приемник, источник — сохраняет в приемнике минимальное из каждой пары сравниваемых вещественных чисел;

FEMMS — более быстрая версия команды EMMS;

PREFETCH источник — заполняет строку кэша L1 из памяти по адресу, указанному источником;

PREFETCHW источник — заполняет строку кэша L1 из памяти по адресу, указанному источником, и помечает как модифицированную.


Расширение IА ММХ


Начиная с модификации процессора Pentium Р54С, все процессоры Intel содержат расширение ММХ, предназначенное для увеличения эффективности программ, работающих с большими потоками данных (обработка изображений, видео, синтез и обработка звука), то есть для всех тех случаев, когда нужно выполнить несложные операции над большими массивами однотипных чисел. ММХ предоставляет несколько новых типов данных, регистров и команд, позволяющих выполнять арифметические и логические операции над несколькими числами одновременно.



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


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


Рис. 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



Регистры FPU


FPU предоставляет восемь регистров для хранения данных и пять вспомогательных регистров.

Регистры данных (R0 – R7) не адресуются по именам, как регистры основного процессора. Вместо этого эти восемь регистров рассматриваются как стек, вершина которого называется ST, а более глубокие элементы — ST(1), ST(2) и так далее до ST(7). Если, например, в какой-то момент времени регистр R5 называется ST (рис. 13), то после записи в этот стек числа оно будет записано в регистр R4, который станет называться ST, R5 станет называться ST(1) и т.д.


Рис. 13. Регистры FPU

К регистрам R0 – R7 нельзя обращаться напрямую, по именам, но если процессор поддерживает расширение ММХ, то мантиссы, находящиеся в этих регистрах, становятся доступны, как ММ0 – ММ7.

Регистр состояний SR содержит слово состояния FPU:

Бит 15: В — занятость FPU — этот флаг существует для совместимости с 8087, и его значение всегда совпадает с ES.

Бит 14: С3 — условный флаг 3.

Биты 13 – 11: ТОР — число от 0 до 7, показывающее, какой из регистров данных R0 – R7 в настоящий момент является вершиной стека.

Бит 10: С2 — условный флаг 2.

Бит 9: С1 — условный флаг 1.

Бит 8: С0 — условный флаг 0.

Бит 7: ES — общий флаг ошибки — равен 1, если произошло хотя бы одно немаскированное исключение.

Бит 6: SF — ошибка стека. Если С1 = 1, произошло переполнение (команда пыталась писать в непустую позицию в стеке), если С1 = 0, произошло антипереполнение (команда пыталась считать число из пустой позиции в стеке).

Бит 5: РЕ — флаг неточного результата — результат не может быть представлен точно.

Бит 4: UE — флаг антипереполнения — результат слишком маленький.


Бит 3: ОЕ — флаг переполнения — результат слишком большой.

Бит 2: ZE — флаг деления на ноль — выполнено деление на ноль.

Бит 1: DE — флаг денормализованного операнда — выполнена операция над денормализованным числом.

Бит 0: IE — флаг недопустимой операции — произошла ошибка стека (SF = 1) или выполнена недопустимая операция.

Биты С0 –  С3 употребляются так же, как и биты, состояния в основном процессоре, — их значения отражают результат выполнения предыдущей команды и используются для условных переходов; команды

fstsw ax sahf

копируют их значения в регистр FLAGS так, что флаг С0 переходит в CF, С2 — в PF, а С3 — в ZF (флаг С2 теряется).

Биты 0 – 5 отражают различные ошибочные ситуации, которые могут возникать при выполнении команд FPU. Они рассмотрены в описании управляющих регистров.

Регистр управления CR:

Биты 15 – 13 — зарезервированы.

Бит 12 «IC» — управление бесконечностью (поддерживается для совместимости с 8087 и 80287 — вне зависимости от значения этого бита +
> -
).

Биты 11 – 10 «RC» — управление округлением.

Биты 9 – 8 «PC» — управление точностью.

Биты 7 – 6 — зарезервированы.

Бит 5 «РМ» — маска неточного результата.

Бит 4 «UM» — маска антипереполнения.

Бит 3 «ОМ» — маска переполнения.

Бит 2 «ZM» — маска деления на ноль.

Бит 1 «DM» — маска денормализованного операнда.

Бит 0 «IM» — маска недействительной операции.

Биты RC определяют способ округления результатов команд FPU до заданной точности (табл. 10).



Таблица 10. Способы округления

Значение RC Способ округления
0 к ближайшему числу
1 к отрицательной бесконечности
2 к положительной бесконечности
3 к нулю
Биты PC определяют точность результатов команд FADD, FSUB, FSUBR, FMUL, FDIV, FDIVR и FSQRT (табл. 11).

Таблица 11. Точность результатов

Значение PC Точность результатов
0 одинарная точность (32-битные числа)
1 зарезервировано
2 двойная точность (64-битные числа)
3 расширенная точность (80-битные числа)
Биты 0 – 5 регистра CR маскируют соответствующие исключения — если маскирующий бит установлен, исключения не происходит, а результат вызвавшей его команды определяется правилами для каждого исключения специально.

Регистр тегов TW содержит восемь пар бит, описывающих содержание каждого регистра данных, — биты 15 – 14 описывают регистр R7, 13 – 12 — R6 и т.д. Если пара бит (тег) равна 11, соответствующий регистр пуст. 00 означает, что регистр содержит число, 01 — ноль, 10 — нечисло, бесконечность, денормализованное число, неподдерживаемое число.

Регистры FIP и FDP содержат адрес последней выполненной команды (кроме FINIT, FCLEX, FLDCW, FSTCW, FSTSW, FSTSWAX, FSTENV, FLDENV, FSAVE, FRSTOR и FWAIT) и адрес ее операнда соответственно и используются в обработчиках исключений для анализа вызвавшей его команды.


Регистры ММХ


Расширение ММХ включает в себя восемь 64-битных регистров общего пользования ММ0 – ММ7, показанных на рис. 14.


Рис. 14. Регистры ММХ

Физически никаких новых регистров с введением ММХ не появилось — ММ0 – ММ7 — это в точности мантиссы восьми регистров FPU, от R0 до R7. При записи числа в регистр ММХ оно появляется в битах 63 – 0 соответствующего регистра FPU, а экспонента (биты 78 – 64) и ее знаковый бит (бит 79) заполняются единицами. Запись числа в регистр FPU также приводит к изменению соответствующего регистра ММХ. Любая команда ММХ, кроме EMMS, приводит к тому, что поле ТОР регистра SR и весь регистр TW в FPU обнуляются. Команда EMMS заполняет регистр TW единицами. Таким образом, нельзя одновременно пользоваться командами для работы с числами с плавающей запятой и командами ММХ, а если это необходимо — следует пользоваться командами FSAVE/FRSTQR,каждый раз перед переходом от использования FPU к ММХ и обратно (эти команды сохраняют состояние регистров ММХ точно так же, как и FPU).



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


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).


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

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



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


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

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



Сдвиговые операции


Команда: SAR приемник, счетчик
Назначение: Арифметический сдвиг вправо
Команда: SAL приемник, счетчик
Назначение: Арифметический сдвиг влево
Команда: SHR приемник, счетчик
Назначение: Логический сдвиг вправо
Команда: SHL приемник, счетчик
Назначение: Логический сдвиг влево
Процессор: 8086

Эти четыре команды выполняют двоичный сдвиг приемника (регистр или переменная) вправо (в сторону старшего бита) или влево (в сторону младшего бита) на значение счетчика (число или регистр CL, из которого учитываются только младшие пять бит, которые могут принимать значения от 0 до 31), Операция сдвига на 1 эквивалентна умножению (сдвиг влево) или делению (сдвиг вправо) на 2. Так, число 0010b (2) после сдвига на 1 влево превращается в 0100b (4). Команды SAL и SHL выполняют одну и ту же операцию (на самом деле это одна и та же команда) — на каждый шаг сдвига старший бит заносится в CF, все биты сдвигаются влево на одну позицию, и младший бит обнуляется. Команда SHR выполняет прямо противоположную операцию: младший бит заносится в CF, все биты сдвигаются на 1 вправо, старший бит обнуляется. Эта команда эквивалентна беззнаковому целочисленному делению на 2. Команда SAR действует по аналогии с SHR, только старший бит не обнуляется, а сохраняет предыдущее значение, так что, например, число 11111100b (-4) перейдет в 11111110b (-2). SAR, таким образом, эквивалентна знаковому делению на 2, но, в отличие от IDIV, округление происходит не в сторону нуля, а в сторону отрицательной бесконечности. Так, если разделить -9 на 4 с помощью IDIV, результат будет -2 (и остаток -1), а если выполнить арифметический сдвиг вправо числа -9 на 2, результат будет -3. Сдвиги больше чем на 1 эквивалентны соответствующим сдвигам на 1, выполненным последовательно. Схема всех сдвиговых операций приведена на рис. 7.


Рис. 7. Сдвиговые операции

Сдвиги на 1 изменяют значение флага OF: SAL/SHL устанавливают его в 1, если после сдвига старший бит изменился (то есть старшие два бита исходного числа не были одинаковыми), и в 0, если старший бит остался тем же. SAR устанавливает OF в 0, и SHR устанавливает OF в значение старшего бита исходного числа. Для сдвигов на несколько бит значение OF не определено. Флаги SF, ZF, PF устанавливаются всеми сдвигами в соответствии с результатом, значение AF не определено (кроме случая, если счетчик сдвига равен нулю, в котором ничего не происходит и флаги не изменяются).


В процессорах 8086 непосредственно можно было задавать в качестве второго операнда только число 1 и при использовании CL учитывать все биты, а не только младшие 5, но уже начиная с 80186 эти команды приняли свой окончательный вид.

Команда: SHRD приемник, источник, счетчик
Назначение: Сдвиг повышенной точности вправо
Команда: SHLD приемник, источник, счетчик
Назначение: Сдвиг повышенной точности влево
Процессор: 80386
Приемник (регистр или переменная) сдвигается влево (в случае SHLD) или вправо (в случае SHRD) на число бит, указанное в счетчике (число или регистр CL, откуда используются только младшие 5 бит, которые могут принимать значения от 0 до 31). Старший (для SHLD) или младший (в случае SHRD) бит не обнуляется, а считывается из источника (регистр), значение которого не изменяется. Например, если приемник содержал 00101001b, источник 1010b, счетчик равен 3, SHRD даст в результате 01000101b, a SHLD — 01001101b (см. рис. 8).



Рис. 8. Сдвиги двойной точности

Флаг OF устанавливается при сдвигах на 1 бит, если изменился знак приемника, и сбрасывается, если знак не изменился; при сдвигах на несколько бит флаг OF не определен. Во всех случаях SF, ZF и PF устанавливаются в соответствии с результатом и AF не определен, кроме случая со сдвигом на 0 бит, в котором значения флагов не изменяются. Если счетчик больше, чем разрядность приемника, — результат и все флаги не определены.

Команда: ROR приемник, счетчик
Назначение: Циклический сдвиг вправо
Команда: ROL приемник, счетчик
Назначение: Циклический сдвиг влево
Команда: RCR приемник, счетчик
Назначение: Циклический сдвиг вправо через флаг переноса
Команда: RCL приемник, счетчик
Назначение: Циклический сдвиг влево через флаг переноса
Процессор: 8086
Эти команды осуществляют циклический сдвиг приемника (регистр или переменная) на число бит, указанное в счетчике (число или регистр CL, из которого учитываются только младшие пять бит, принимающие значения от 0 до 31). При выполнении циклического сдвига на 1 команды ROR (ROL) сдвигают каждый бит приемника вправо (влево) на одну позицию, за исключением самого младшего (старшего), который записывается в позицию самого старшего (младшего) бита. Команды RCR и RCL выполняют аналогичное действие, но включают флаг CF в цикл, как если бы он был дополнительным битом в приемнике (рис. 9).



Рис. 9. Циклические сдвиги

После выполнения команд циклического сдвига флаг CF всегда равен последнему вышедшему за пределы приемника биту, флаг OF определен только для сдвигов на 1 — он устанавливается, если изменилось значение самого старшего бита, и сбрасывается, если старший бит не изменился. Флаги SF, ZF, AF и PF не изменяются.


Сдвиговые операции ММХ


Команда: PSLLW приемник,источник
PSLLD приемник,источник
PSLLQ приемник,источник
Назначение: Логический сдвиг влево
Процессор: ММХ

Команды сдвигают влево биты в каждом элементе (в словах — для PSLLW, в двойных словах — для PSLLD, во всем регистре — для PSLLQ) приемника (регистр ММХ) на число бит, указанное в источнике (8-битное число, регистр ММХ или переменная). При сдвиге младшие биты заполняются нулями, так что, например, команды

psllw mm0,15 pslld mm0,31 psllq mm0,63

обнуляют регистр ММ0.

Команда: PSRLW приемник,источник
PSRLD приемник,источник
PSRLQ приемник,источник
Назначение: Логический сдвиг вправо
Процессор: ММХ

Команды сдвигают вправо биты в каждом элементе (в словах — для PSRLW, в двойных словах — для PSRLD, во всем регистре — для PSRLQ) приемника (регистр ММХ) на число бит, указанное в источнике (8-битное число, регистр ММХ или переменная). При сдвиге старшие биты заполняются нулями.

Команда: PSRAW приемник,источник
PSRAD приемник,источник
Назначение: Арифметический сдвиг вправо
Процессор: ММХ

Команды сдвигают вправо биты в каждом элементе (в словах — для PSRAW и в двойных словах — для PSRAD) приемника (регистр ММХ) на число бит, указанное в источнике (8-битное число, регистр ММХ или переменная). При сдвиге самый старший (знаковый) бит используется для заполнения пустеющих старших бит, так что фактически происходит знаковое деление на 2 в степени, равной содержимому источника.



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


При использовании каждой из сегментированных моделей памяти для формирования любого адреса применяются два числа — адрес начала сегмента и смещение искомого байта относительно этого начала (в бессегментной модели памяти 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 уменьшается.


Рис. 4. Стек

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



Строковые операции


Все команды для работы со строками считают, что строка-источник находится по адресу DS:SI (или DS:ESI), то есть в сегменте памяти, указанном в DS со смещением в SI, а строка-приемник — соответственно в ES:DI (или ES:EDI). Кроме того, все строковые команды работают только с одним элементом строки (байтом, словом или двойным словом) за один раз. Для того чтобы команда выполнялась над всей строкой, необходим один из префиксов повторения операций.

Префикс: REP
Назначение: Повторять
Префикс: REPE
Назначение: Повторять, пока равно
Префикс: REPNE
Назначение: Повторять, пока не равно
Префикс: REPZ
Назначение: Повторять, пока ноль
Префикс: REPNZ
Назначение: Повторять, пока не ноль
Процессор: 8086

Все эти команды — префиксы для операций над строками. Любой из префиксов выполняет следующую за ним команду строковой обработки столько раз, сколько указано в регистре ЕСХ (или СХ, в зависимости от разрядности адреса), уменьшая его при каждом выполнении команды на 1. Кроме того, префиксы REPZ и REPE прекращают повторения команды, если флаг ZF сброшен в 0, и префиксы REPNZ и REPNE прекращают повторения, если флаг ZF установлен в 1. Префикс REP обычно используется с командами INS, OUTS, MOVS, LODS, STOS, а префиксы REPE, REPNE, REPZ и REPNZ — с командами CMPS и SCAS. Поведение префиксов не с командами строковой обработки не определено.

Команда: MOVS приемник, источник
Назначение: Копирование строки
Процессор: 8086
Команда: MOVSB
Назначение: Копирование строки байт
Процессор: 8086
Команда: MOVSW
Назначение: Копирование строки слов
Процессор: 8086
Команда: MOVSD
Назначение: Копирование строки двойных слов
Процессор: 80386

Копирует один байт (MOVSB), слово (MOVSW) или двойное слово (MOVSD) из памяти по адресу DS:ESI (или DS:SI, в зависимости от разрядности адреса) в память по адресу ES:EDI (или ES:DI). При использовании формы записи MOVS ассемблер сам определяет из типа указанных операндов (принято указывать имена копируемых строк, но можно использовать любые два операнда подходящего типа), какую из трех форм этой команды (MOVSB, MOVSW или MOVSD) выбрать. Используя MOVS с операндами, можно заменить регистр DS на другой с помощью префикса замены сегмента (ES:, GS:, FS:, CS:, SS:), регистр ES заменить нельзя. После выполнения команды регистры ESI (SI) и EDI (DI) увеличиваются на 1, 2 или 4 (если копируются байты, слова или двойные слова), если флаг DF = 0, и уменьшаются, если DF = 1. При использовании с префиксом REP команда MOVS выполняет копирование строки длиной в ЕСХ (или СХ) байт, слов или двойных слов.

Команда: CMPS приемник, источник
Назначение: Сравнение строк
Процессор: 8086
Команда: CMPSB
Назначение: Сравнение строк байт
Процессор: 8086
Команда: CMPSW
Назначение: Сравнение строк слов
Процессор: 8086
Команда: CMPSD
Назначение: Сравнение строк двойных слов
Процессор: 80386
<
/p> Сравнивает один байт (CMPSB), слово (CMPSW) или двойное слово (CMPSD) из памяти по адресу DS:ESI (или DS:SI, в зависимости от разрядности адреса) с байтом, словом или двойным словом по адресу ES:EDI (или ES:DI) и устанавливает флаги аналогично команде СМР. При использовании формы записи CMPS ассемблер сам определяет из типа указанных операндов (принято указывать имена сравниваемых строк, но можно использовать любые два операнда подходящего типа), какую из трех форм этой команды (CMPSB, CMPSW или CMPSD) выбрать. Используя CMPS с операндами, можно заменить регистр DS на другой, применяя префикс замены сегмента (ES:, GS:, FS:, CS:, SS:), регистр ES заменить нельзя. После выполнения команды регистры ESI (SI) и EDI (DI) увеличиваются на 1, 2 или 4 (если сравниваются байты, слова или двойные слова), если флаг DF = 0, и уменьшаются, если DF = 1. При использовании с префиксом REP команда CMPS выполняет сравнение строки длиной в ЕСХ (или СХ) байт, слов или двойных слов, но чаще ее используют с префиксами REPNE/REPNZ или REPE/REPZ. В первом случае сравнение продолжается до первого несовпадения в сравниваемых строках, а во втором — до первого совпадения.

Команда: SCAS приемник
Назначение: Сканирование строки
Процессор: 8086
Команда: SCASB
Назначение: Сканирование строки байт
Процессор: 8086
Команда: SCASW
Назначение: Сканирование строки слов
Процессор: 8086
Команда: SCASD
Назначение: Сканирование строки двойных слов
Процессор: 80386
Сравнивает содержимое регистра AL (SCASB), AX (SCASW) или ЕАХ (SCASD) с байтом, словом или двойным словом из памяти по адресу ES:EDI (или ES:DI, в зависимости от разрядности адреса) и устанавливает флаги аналогично команде СМР. При использовании формы записи SCAS ассемблер сам определяет из типа указанного операнда (принято указывать имя сканируемой строки, но можно использовать любой операнд подходящего типа), какую из трех форм этой команды (SCASB, SCASW или SCASD) выбрать. После выполнения команды регистр EDI (DI) увеличивается на 1, 2 или 4 (если сканируются байты, слова или двойные слова), если флаг DF = 0, и уменьшается, если DF = 1. При использовании с префиксом REP команда SCAS выполняет сканирование строки длиной в ЕСХ (или СХ) байт, слов или двойных слов, но чаще ее используют с префиксами REPNE/REPNZ или REPE/REPZ. В первом случае сканирование продолжается до первого элемента строки, отличного от содержимого аккумулятора, а во втором — до первого совпадающего.

Команда: LODS источник
Назначение: Чтение из строки
Процессор: 8086
Команда: LODSB
Назначение: Чтение байта из строки
Процессор: 8086
Команда: LODSW
Назначение: Чтение слова из строки
Процессор: 8086
Команда: LODSD
Назначение: Чтение двойного слова из строки
Процессор: 80386
<


/p> Копирует один байт (LODSB), слово (LODSW) или двойное слово (LODSD) из памяти по адресу DS:ESI (или DS:SI, в зависимости от разрядности адреса) в регистр AL, АХ или ЕАХ соответственно. При использовании формы записи LODS ассемблер сам определяет из типа указанного операнда (принято указывать имя строки, но можно использовать любой операнд подходящего типа), какую из трех форм этой команды (LODSB, LODSW или LODSD) выбрать. Используя LODS с операндом, можно заменить регистр DS на другой с помощью префикса замены сегмента (ES:, GS:, FS:, CS:, SS:). После выполнения команды регистр ESI (SI) увеличивается на 1, 2 или 4 (если считывается байт, слово или двойное слово), если флаг DF = 0, и уменьшается, если DF = 1. При использовании с префиксом REP команда LODS выполнит копирование строки длиной в ЕСХ (или СХ), что приведет к тому, что в аккумуляторе окажется последний элемент строки. На самом деле эту команду используют без префиксов, часто внутри цикла в паре с командой STOS, так что LODS считывает число, другие команды выполняют над ним какие-нибудь действия, а затем STOS записывает измененное число в то же место в памяти.

Команда: STOS приемник
Назначение: Запись в строку
Процессор: 8086
Команда: STOSB
Назначение: Запись байта в строку
Процессор: 8086
Команда: STOSW
Назначение: Запись слова в строку
Процессор: 8086
Команда: STOSD
Назначение: Запись двойного слова в строку
Процессор: 80386
Копирует регистр AL (STOSB), AX (STOSW) или ЕАХ (STOSD) в память по адресу ES:EDI (или ES:DI, в зависимости от разрядности адреса). При использовании формы записи STOS ассемблер сам определяет из типа указанного операнда (принято указывать имя строки, но можно использовать любой операнд подходящего типа), какую из трех форм этой команды (STOSB, STOSW или STOSD) выбрать. После выполнения команды регистр EDI (DI) увеличивается на 1, 2 или 4 (если копируется байт, слово или двойное слово), если флаг DF = 0, и уменьшается, если DF = 1. При использовании с префиксом REP команда STOS заполнит строку длиной в ЕСХ (или СХ) числом, находящимся в аккумуляторе.

Команда: INS источник, DX
Назначение: Чтение строки из порта
Процессор: 80186
Команда: INSB
Назначение: Чтение строки байт из порта
Процессор: 80186
Команда: INSW
Назначение: Чтение строки слов из порта
Процессор: 80186
Команда: INSD
Назначение: Чтение строки двойных слов из порта
Процессор: 80386
<


/p> Считывает из порта ввода-вывода, номер которого указан в регистре DX, байт (INSB), слово (INSW) или двойное слово (INSD) в память по адресу ES:EDI (или ES:DI, в зависимости от разрядности адреса). При использовании формы записи INS ассемблер определяет из типа указанного операнда, какую из трех форм этой команды (INSB, INSW или INSD) употребить. После выполнения команды регистр EDI (DI) увеличивается на 1, 2 или 4 (если считывается байт, слово или двойное слово), если флаг DF = 0, и уменьшается, если DF = 1. При использовании с префиксом REP команда INS считывает блок данных из порта длиной в ЕСХ (или СХ) байт, слов или двойных слов.

Команда: OUTS DX, приемник
Назначение: Запись строки в порт
Процессор: 80186
Команда: OUTSB
Назначение: Запись строки байт в порт
Процессор: 80186
Команда: OUTSW
Назначение: Запись строки слов в порт
Процессор: 80186
Команда: OUTSD
Назначение: Запись строки двойных слов в порт
Процессор: 80386
Записывает в порт ввода-вывода, номер которого указан в регистре DX, байт (OUTSB), слово (OUTSW) или двойное слово (OUTSD) из памяти по адресу DS:ESI (или DS:SI, в зависимости от разрядности адреса). При использовании формы записи OUTS ассемблер определяет из типа указанного операнда, какую из трех форм этой команды (OUTSB, OUTSW или OUTSD) употребить. Используя OUTS с операндами, также можно заменить регистр DS на другой с помощью префикса замены сегмента (ES:, GS:, FS:, CS:, SS:). После выполнения команды регистр ESI (SI) увеличивается на 1, 2 или 4 (если считывается байт, слово или двойное слово), если флаг DF = 0, и уменьшается, если DF = 1. При использовании с префиксом REP команда OUTS записывает блок данных размером в ЕСХ (или СХ) байт, слов или двойных слов в указанный порт. Все процессоры вплоть до Pentium не проверяли готовность порта принять новые данные в ходе выполнения команды REP OUTS, так что, если порт не успевал обрабатывать информацию с той скоростью, с которой ее поставляла эта команда, часть данных терялась.


Типы данных FPU


Числовой процессор может выполнять операции с семью разными типами данных, представленными в таблице 9, — три целых двоичных, один целый десятичный и три типа данных с плавающей запятой.

Таблица 9. Типы данных FPU

Тип данных Бит Количество значащих цифр Пределы
Целое слово 16 4 -32768 — 32767
Короткое целое 32 9 -2*109 — 2*109
Длинное целое 64 18 -9*1018 — 9*1018
Упакованное десятичное 80 18 -99..99 — +99..99 (18 цифр)
Короткое вещественное 32 7 1.18*10-38 — 3.40*1038
Длинное вещественное 64 15—16 2.23*10-308 — 1.79*10308
Расширенное вещественное 80 19 3.37*10-4932 — 1.18*104932

Вещественные числа хранятся, как и все данные, в форме двоичных чисел. Двоичная запись числа с плавающей запятой аналогична десятичной, только позиции справа от запятой соответствуют не делению на 10 в соответствующей степени, а делению на 2. Переведем для примера в двоичный вид число 0,625:

0,625 - 1/2 = 0,125

1/4 больше, чем 0,125

0,125 - 1/8 = 0

Итак, 0,625 = 0,101b. При записи вещественных чисел всегда выполняют нормализацию — умножают число на такую степень двойки, чтобы перед десятичной точкой стояла единица, в нашем случае

0,625 = 0,101b = 1,01b * 2-1

Говорят, что число имеет мантиссу 1,01 и экспоненту -1. Как можно заметить, при использовании этого алгоритма первая цифра мантиссы всегда равна 1, так что ее можно не писать, увеличивая тем самым точность представления числа дополнительно на 1 бит. Кроме того, значение экспоненты хранят не в виде целого со знаком, а в виде суммы с некоторым числом так, чтобы хранить всегда только положительное число и чтобы было легко сравнивать вещественные числа — в большинстве случаев достаточно сравнить экспоненту. Теперь мы можем рассмотреть вещественные форматы IEEE, используемые в процессорах Intel:

короткое вещественное: бит 31 — знак мантиссы, биты 30 – 23 — 8-битная экспонента + 127, биты 22 – 0 — 23-битная мантисса без первой цифры;


длинное вещественное: бит 63 — знак мантиссы, биты 62 – 52 — 11-битная экспонента + 1024, биты 51 – 0 — 52-битная мантисса без первой цифры;

расширенное вещественное: бит 79 — знак мантиссы, биты 78 – 64 — 15-битная экспонента + 16 383, биты 63 – 0 — 64-битная мантисса с первой цифрой ( то есть бит 63 равен 1).

FPU выполняет все вычисления в 80-битном расширенном формате, а 32- и 64-битные числа используются для обмена данными с основным процессором и памятью.

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

положительный ноль: все биты числа сброшены в ноль;

отрицательный ноль: знаковый бит — 1, все остальные биты — нули;

положительная бесконечность: знаковый бит — 0, все биты мантиссы — 0, все биты экспоненты — 1;

отрицательная бесконечность: знаковый бит — 1, все биты мантиссы — 0, все биты экспоненты — 1;

денормализованные числа: все биты экспоненты — 0 (используются для работы с очень маленькими числами — до 10-16445 для расширенной точности);

неопределенность: знаковый бит — 1, первый бит мантиссы (первые два для 80-битных чисел) — 1, а остальные — 0, все биты экспоненты — 1;

не-число типа SNAN (сигнальное): все биты экспоненты — 1, первый бит мантиссы — 0 (для 80-битных чисел первые два бита мантиссы — 10), а среди остальных бит есть единицы;

не-число типа QNAN (тихое): все биты экспоненты — 1, первый бит мантиссы (первые два для 80-битных чисел) — 1, среди остальных бит есть единицы. Неопределенность — один из вариантов QNAN;

неподдерживаемое число: все остальные ситуации.

Остальные форматы данных FPU также допускают неопределенность — единица в старшем бите и нули в остальных для целых чисел, и старшие 16 бит — единицы для упакованных десятичных чисел.


Типы данных ММХ


ММХ использует четыре новых типа данных:

учетверенное слово — простое 64-битное число;

упакованные двойные слова — два 32-битных двойных слова, упакованные в 64-битный тип данных. Двойное слово 1 занимает биты 63 – 32, и двойное слово 0 занимает биты 31 – 0;

упакованные слова — четыре 16-битных слова, упакованные в 64-битный тип данных. Слово 3 занимает биты 63 – 48, слово 0 занимает биты 15 – 0;

упакованные байты — восемь байт, упакованных в 64-битный тип данных. Байт 7 занимает биты 63 – 56, байт 0 занимает биты 7 – 0.

Команды ММХ перемещают упакованные данные в память или в обычные регистры как целое, но выполняют арифметические и логические операции над каждым элементом по отдельности.

Арифметические операции в ММХ могут использовать специальный способ обработки переполнений и антипереполнений — насыщение. Если результат операции больше, чем максимальное значение для его типа данных (+127 для байта со знаком), то результат считают равным этому максимальному значению. Если он меньше минимального значения — соответственно его полагают равным минимально допустимому значению. Например, при операциях с цветом насыщение позволяет ему превращаться в чисто белый при переполнении и в чисто черный при антипереполнении, в то время как обычная арифметика привела бы к нежелательной инверсии цвета.



Трансцендентные операции FPU


Команда: FSIN
Назначение: Синус
Процессор: 80387

Вычисляет синус числа, находящегося в ST(0), и сохраняет результат в этом же регистре. Операнд считается заданным в радианах и не может быть больше 263 или меньше -263 (можно воспользоваться FPREM с делителем 2

, если операнд слишком велик). Если операнд выходит за эти пределы, флаг С2 устанавливается в 1 и значение ST(0) не изменяется.

Команда: FCOS
Назначение: Косинус
Процессор: 80387

Вычисляет косинус числа, находящегося в ST(0), и сохраняет результат в этом же регистре. Операнд считается заданным в радианах и не может быть больше 263 или меньше -263 (так же, как и в случае синуса, можно воспользоваться FPREM с делителем 2

, если операнд слишком велик). Если операнд выходит за эти пределы, флаг С2 устанавливается в 1 и значение ST(0) не изменяется.

Команда: FSINCOS
Назначение: Синус и косинус
Процессор: 80387

Вычисляет синус и косинус числа, находящегося в ST(0), помещает синус в ST(0) и затем помещает косинус в стек (так что синус оказывается в ST(1), косинус — в ST(0), и ТОР уменьшается на 1). Операнд считается заданным в радианах и не может быть больше 263 или меньше -263. Если операнд выходит за эти пределы, флаг С2 устанавливается в 1 и значение ST(0) и стек не изменяются.

Команда: FPTAN
Назначение: Тангенс
Процессор: 8087

Вычисляет тангенс числа, находящегося в регистре ST(0), заменяет его на вычисленное значение и затем помещает 1 в стек, так что результат оказывается в ST(1), ST(0) содержит 1, а ТОР уменьшается на единицу. Как и для остальных тригонометрических команд, операнд считается заданным в радианах и не может быть больше 263 или меньше -263. Если операнд выходит за эти пределы, флаг С2 устанавливается в 1 и значение ST(0) и стек не изменяются. Единица помещается в стек для того, чтобы можно было получить котангенс вызовом команды FDIVR сразу после FPTAN.

Команда: FPATAN
Назначение: Арктангенс
Процессор: 8087

Вычисляет арктангенс числа, получаемого при делении ST(1) на ST(0), сохраняет результат в ST(1) и выталкивает ST(0) из стека (помечает ST(0) как пустой и увеличивает TOP на 1). Рeзyльтaт всегда имеет тот же знак, что и ST(1), и меньше

по абсолютной величине. Смысл этой операции в том, что FPATAN вычисляет угол между осью абсцисс и линией, проведенной из центра координат в точку ST(1),ST(0).


FPATAN может выполняться над любыми операндами (кроме не-чисел), давая результаты для различных нулей и бесконечностей, определенные в соответствии со стандартом IEEE (как показано в табл. 17).

Таблица 17. Результаты работы команды FPATAN1

  ST(0)

ST(1)
  -
-F 0 0 +F +
-
-3
/4
-
/2
-
/2
-
/2
-
/2
-
/4
-F -
от -
до -
/2
-
/2
-
/2
от -
/2 до -0
0
0 -
-
-
0 0 0
0 +
+
+
0 0 0
+F +
от +
до +
/2
+
/2
+
/2
от +
/2 до +0
0
+
+3
/4
+
/2
+
/2
+
/2
+
/2
+
/4
1 F в этой таблице — конечное вещественное число.

Команда: F2XMI
Назначение: Вычисление 2х-1
Процессор: 8087
Возводит 2 в степень, равную ST(0), и вычитает 1. Результат сохраняется в ST(0). Значение ST(0) должно лежать в пределах от -1 до +1, иначе результат не определен.

Команда: FYL2X
Назначение: Вычисление у*log2(x)
Процессор: 8087
Вычисляет ST(1)*log2(ST(0)), помещает результат в ST(1) и выталкивает ST(0) из стека, так что после этой операции результат оказывается в ST(0). Первоначальное значение ST(0) должно быть неотрицательным. Если регистр ST(0) содержал ноль, результат (если ZM = 1) будет равен бесконечности со знаком, обратным ST(1).

Команда: FYL2XP1
Назначение: Вычисление у*log2(x+1)
Процессор: 8087
Вычисляет ST(1)*log2(ST(0)+1), помещает результат в ST(1) и выталкивает ST(0) из стека, так что после этой операции результат оказывается в ST(0). Первоначальное значение ST(0) должно быть в пределах от -(1 -
/2) до (1 +
/2), иначе результат не определен. Команда FYL2XP1 дает большую точность для ST(0), близких к нулю, чем FYL2X для суммы того же ST(0) и 1.


Управление флагами


Команда: STC
Назначение: Установить флаг переноса
Процессор: 8086

Устанавливает флаг CF в 1.

Команда: CLC
Назначение: Сбросить флаг переноса
Процессор: 8086

Сбрасывает флаг CF в 0.

Команда: CMC
Назначение: Инвертировать флаг переноса
Процессор: 8086

Инвертирует флаг СF.

Команда: STD
Назначение: Установить флаг направления
Процессор: 8086

Устанавливает флаг DF в 1, так что при последующих строковых операциях регистры DI и SI будут уменьшаться.

Команда: CLD
Назначение: Сбросить флаг направления
Процессор: 8086

Сбрасывает флаг DF в 0, так что при последующих строковых операциях регистры DI и SI будут увеличиваться.

Команда: LAHF
Назначение: Загрузить флаги состояния в АН
Процессор: 8086

Копирует младший байт регистра FLAGS в АН, включая флаги SF (бит 7), ZF (бит 6), AF (бит 4), PF (бит 2) и CF (бит 0). Бит 1 устанавливается в 1, биты 3 и 5 — в 0.

Команда: SAHF
Назначение: Загрузить флаги состояния из АН
Процессор: 8086

Загружает флаги SF, ZF, AF, PF и CF из регистра АН значениями бит 7, 6, 4, 2 и 0 соответственно. Зарезервированные биты 1, 3 и 5 регистра флагов не изменяются.

Команда: PUSHF
Назначение: Поместить FLAGS в стек
Процессор: 8086
Команда: PUSHFD
Назначение: Поместить ЕFLAGS в стек
Процессор: 80386

Эти команды копируют содержание регистра FLAGS или EFLAGS в стек (уменьшая SP или ESP на 2 или 4 соответственно). При копировании регистра EFLAGS флаги VM и RF (биты 16 и 17) не копируются, соответствующие биты в двойном слове, помещенном в стек, обнуляются.

Команда: POPF
Назначение: Загрузить FLAGS из стека
Процессор: 8086
Команда: POPFD
Назначение: Загрузить EFLAGS из стека
Процессор: 80386

Считывает из вершины стека слово (POPF) или двойное слово (POPFD) и помещает в регистр FLAGS или EFLAGS. Эффект этих команд зависит от режима, в котором выполняется программа: в реальном режиме и в защищенном режиме с уровнем привилегий 0 модифицируются все незарезервированные флаги в EFLAGS, кроме VIP, VIF и VM. VIP и VIF обнуляются, и VM не изменяется. В защищенном режиме c уровнем привилегий, большим нуля, но меньшим или равным IOPL, модифицируются все флаги, кроме VIP, VIF, VM и IOPL. В режиме V86 не модифицируются флаги VIF, VIP, VM, IOPL и RF.

Команда: CLI
Назначение: Запретить прерывания
Процессор: 8086
<
/p> Сбрасывает флаг IF в 0. После выполнения этой команды процессор игнорирует все прерывания от внешних устройств (кроме NMI). В защищенном режиме эта команда, так же как и все другие команды, модифицирующие флаг IF (POPF или IRET), выполняется, только если программе даны соответствующие привилегии (CPL < IOPL).

Команда: STI
Назначение: Разрешить прерывания
Процессор: 8086
Устанавливает флаг IF в 1, отменяя тем самым действие команды CLI.

Команда: SALC
Назначение: Установить AL в соответствии с CF
Процессор: 8086
Устанавливает AL в 0FFh, если флаг CF = 1, и сбрасывает в 00h, если CF = 0. Это недокументированная команда с кодом 0D6h, присутствующая во всех процессорах Intel и совместимых с ними (начиная с 8086). В документации на Pentium Pro эта команда упоминается в общем списке команд, но ее действие не описывается. Действие SALC аналогично SBB AL,AL, но SALC не изменяет значений флагов.


Загрузка сегментных регистров


Команда: LDS приемник, источник
Назначение: Загрузить адрес, используя DS
Процессор: 8086
Команда: LES приемник, источник
Назначение: Загрузить адрес, используя ES
Процессор: 8086
Команда: LFS приемник, источник
Назначение: Загрузить адрес, используя FS
Процессор: 80386
Команда: LGS приемник, источник
Назначение: Загрузить адрес, используя GS
Процессор: 80386
Команда: LSS приемник, источник
Назначение: Загрузить адрес, используя SS
Процессор: 8086

Второй операнд (источник) для всех этих команд — переменная в памяти размером в 32 или 48 бит (в зависимости от разрядности операндов). Первые 16 бит из этой переменной загружаются в соответствующий сегментный регистр (DS для LDS, ES для LES и т.д.), а следующие 16 или 32 — в регистр общего назначения, указанный в качестве первого операнда. В защищенном режиме значение, загружаемое в сегментный регистр, всегда должно быть правильным селектором сегмента (в реальном режиме любое число может использоваться как селектор).