Минимальная единица информации называется битом. Бит может принимать только два значения — обычно 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. Все примеры программ в этой книге построены так, что можно использовать любой из этих компиляторов.
Практически все существующие сейчас компьютерные системы, включая 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 приемник,источник |
Назначение: | Вычитанние |
Процессор: | ММХ |
Команда: | PSUBSB приемник,источник PSUBSW приемник,источник |
Назначение: | Вычитание с насыщением |
Процессор: | ММХ |
Команда: | PSUBUSB приемник,источник PSUBUSW приемник,источник |
Назначение: | Беззнаковое вычитание с насыщением |
Процессор: | ММХ |
Команда: | PMULHW приемник,источник |
Назначение: | Старшее умножение |
Процессор: | ММХ |
Команда: | PMULLW приемник,источник |
Назначение: | Младшее умножение |
Процессор: | ММХ |
Команда: | PMADDWD приемник,источник |
Назначение: | Умножение и сложение |
Процессор: | ММХ |
Команда: | 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
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. | ||
Команда: | AAA |
Назначение: | ASCII-коррекция после сложения |
Процессор: | 8086 |
Команда: | AAS |
Назначение: | ASCII-коррекция после вычитания |
Процессор: | 8086 |
Команда: | AAM |
Назначение: | ASCII-коррекция после умножения |
Процессор: | 8086 |
Код команды ААМ — D4h 0Ah, где 0Ah — основание системы счисления, по отношению к которой выполняется коррекция. Этот байт можно заменить на любое другое число (кроме нуля), и ААМ преобразует АХ к двум неупакованным цифрам любой системы счисления. Такая обобщенная форма ААМ работает на всех процессорах (начиная с 8086), но появляется в документации Intel только с процессоров Pentium. Фактически действие, которое выполняет ААМ, — целочисленное деление AL на 0Ah (или любое другое число в общем случае), частное помещается в AL, и остаток — в АН, так что эту команду часто используют для быстрого деления в высокооптимизированных алгоритмах. | ||
Команда: | AAD |
Назначение: | ASCII-коррекция перед делением |
Процессор: | 8086 |
Так же как и команда ААМ, 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 поддерживается.
Производитель | Строка в ЕВХ:ЕСХ:ЕРХ |
Intel | GenuineIntel |
UMC | UMC UMC UMC |
Cyrix | CyrixInstead |
AMD | AuthenticAMD |
NexGen | NexGenDriven |
Centaur Technology | CentaurHalls |
Все команды из этого раздела, кроме команд деления и умножения, изменяют флаги 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 |
Команда: | IMUL источник IMUL приемник, источник IMUL приемник, источник1, источник2 |
Назначение: | Умножение чисел со знаком |
Процессор: | 8086 80386 80186 |
Команда: | MUL источник |
Назначение: | Умножение чисел без знака |
Процессор: | 8086 |
Команда: | IDIV источник |
Назначение: | Целочисленное деление со знаком |
Процессор: | 8086 |
Команда: | DIV источник |
Назначение: | Целочисленное деление без знака |
Процессор: | 8086 |
Команда: | INC приемник |
Назначение: | Инкремент |
Процессор: | 8086 |
Команда: | DEC приемник |
Назначение: | Декремент |
Процессор: | 8086 |
Команда: | NEG приемник |
Назначение: | Изменение знака |
Процессор: | 8086 |
Красивый пример применения команды NEG — получение абсолютного значения числа, используя всего две команды — изменение знака и переход на первую команду еще раз, если знак отрицательный: label0: neg eax js label0 | ||
Команда: | CMP приемник, источник |
Назначение: | Сравнение |
Процессор: | 8086 |
Несмотря на то что условные команды почти всегда применяются сразу после СМР, не надо забывать, что точно так же их можно использовать после любой команды, модифицирующей флаги, например: проверить равенство АХ нулю можно более короткой командой test ax,ax а равенство единице — однобайтной командой dec ax | ||
Команда: | CMPXCHG приемник, источник |
Назначение: | Сравнить и обменять между собой |
Процессор: | 80486 |
Команда: | CMPXCHG8B приемник |
Назначение: | Сравнить и обменять восемь байт |
Процессор: | Р5 |
При выполнении команд 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 |
Код команды | Реальное условие | Условие для 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 | если есть знак |
Команда: | JCXZ метка |
Назначение: | Переход, если СХ = 0 |
Процессор: | 8086 |
Команда: | JECXZ метка |
Назначение: | Переход, если EСХ = 0 |
Процессор: | 80386 |
Команда: | LOOP метка |
Назначение: | Цикл |
Процессор: | 8086 |
Команда: | LOOPE метка |
Назначение: | Цикл, пока равно |
Команда: | LOOPZ метка |
Назначение: | Цикл, пока ноль |
Команда: | LOOPNE метка |
Назначение: | Цикл, пока не равно |
Команда: | LOOPNZ метка |
Назначение: | Цикл, пока не ноль |
Процессор: | 8086 |
Команда: | CALL операнд |
Назначение: | Вызов процедуры |
Процессор: | 8086 |
Команда: | RET число RETN число RETF число |
Назначение: | Возврат из процедуры |
Процессор: | 8086 |
Команда: | INT число |
Назначение: | Вызов прерывания |
Процессор: | 8086 |
Команда: | IRET IRETD |
Назначение: | Возврат из обработчика прерывания |
Процессор: | 8086 |
Команда: | INT3 |
Назначение: | Вызов прерывания 3 |
Процессор: | 8086 |
Команда: | INTO |
Назначение: | Вызов прерывания 4 при переполнении |
Процессор: | 8086 |
Команда: | BOUND индекс, границы |
Назначение: | Проверка выхода за границы массива |
Процессор: | 80186 |
Команда: | ENTER размер, уровень |
Назначение: | Вход в процедуру |
Процессор: | 80186 |
Команда: | LEAVE |
Назначение: | Выход из процедуры |
Процессор: | 80186 |
Команда: | 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 |
Команда: | FCMOVcc приемник, источник |
Назначение: | Условная пересылка данных |
Процессор: | P6 |
Команда | Значения флагов | Действие после 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 переводит единственное старшее двойное слово приемника в учетверенное.
Команда: | PUNPCKLBW приемник,источник PUNPCKLWD приемник,источник PUNPCKLDQ приемник,источник |
Назначение: | Распаковка и объединение младших элементов |
Процессор: | ММХ |
Команда: | 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 |
Команда: | FCOMI источник |
Назначение: | Сравнить и установить EFLAGS |
Команда: | FCOMIP источник |
Назначение: | Сравнить, установить EFLAGS и вытолкнуть |
Команда: | FUCOMI источник |
Назначение: | Сравнить без учета порядков и установить EFLAGS |
Команда: | FUCOMIP источник |
Назначение: | Сравнить без учета порядков, установить EFLAGS и вытолкнуть из стека |
Процессор: | P6 |
Условие | ZF | PF | CF |
ST(0) > источник | 0 | 0 | 0 |
ST(0) < источник | 0 | 0 | 1 |
ST(0) = источник | 1 | 0 | 0 |
Не сравнимы | 1 | 1 | 1 |
Команда: | FTST |
Назначение: | Проверить, не содержит ли SP(0) ноль |
Процессор: | 8087 |
Команда: | FXAM |
Назначение: | Проанализировать содержимое ST(0) |
Процессор: | 8087 |
Тип числа | 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) источника (регистр ММХ или переменная) с соответствующими элементами приемника (регистр ММХ). Если элемент приемника больше, чем соответствующий элемент источника, все биты в этом элементе приемника устанавливаются в единицы. Если элемент приемника меньше или равен элементу источника, он обнуляется.
Команда: | 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 |
Команда: | FLDCW источник |
Назначение: | Загрузить регистр CR |
Процессор: | 8087 |
Команда: | FSTENV приемник |
Назначение: | Сохранить вспомогательные регистры |
Команда: | FNSTENV приемник |
Назначение: | Сохранить вспомогательные регистры без ожидания |
Процессор: | 8087 |
Команда: | FLDENV источник |
Назначение: | Загрузить вспомогательные регистры |
Процессор: | 8087 |
Команда: | FSAVE приемник |
Назначение: | Сохранить состояние FPU |
Команда: | FNSAVE приемник |
Назначение: | Сохранить состояние FPU без ожидания |
Процессор: | 8087 |
Команда: | FXSAVE приемник |
Назначение: | Быстрое сохранение состояния FPU |
Процессор: | PII |
Команда: | FRSTOR источник |
Назначение: | Восстановить состояние FPU |
Процессор: | 8087 |
Команда: | FXRSTOR источник |
Назначение: | Быстрое восстановление состояния FPU |
Процессор: | PII |
Команда: | FSTSW приемник |
Назначение: | Сохранить регистр SR |
Команда: | FNSTSW приемник |
Назначение: | Сохранить регистр SR без ожидания |
Процессор: | 80287 |
Команда: | WAIT FWAIT |
Назначение: | Ожидание готовности FPU |
Процессор: | 8087 |
Команда: | FNOP |
Назначение: | Отсутствие операции |
Процессор: | 8087 |
Команда: | EMMS |
Назначение: | Освободить регистры ММХ |
Процессор: | ММХ |
Если выполнялись какие-нибудь команды ММХ (кроме EMMS), все регистры FPU помечаются как занятые (в регистре TW). Команда EMMS помечает все регистры 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 ах,ах ; обнуление регистра АХ
Команда: | NOT приемник |
Назначение: | Инверсия |
Процессор: | 8086 |
Команда: | TEST приемник, источник |
Назначение: | Логическое сравнение |
Процессор: | 8086 |
Команда: | 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 (если оно указано в регистре).
Команда: | BSF приемник, источник |
Назначение: | Прямой поиск бита |
Команда: | BSR база, смещение |
Назначение: | Обратный поиск бита |
Процессор: | 80386 |
Команда: | SETcc приемник |
Назначение: | Установка байта по условию |
Процессор: | 80386 |
Код команды | Реальное условие | Условие для 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х в ах
Код команды | Реальное условие | Условие для 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 |
Команда: | BSWAP регистр32 |
Назначение: | Обмен байт внутри регистра |
Процессор: | 80486 |
Команда: | PUSH источник |
Назначение: | Поместить данные в стек |
Процессор: | 8086 |
Команда: | POP приемник |
Назначение: | Считать данные из стека |
Процессор: | 8086 |
Команда: | PUSHA PUSHAD |
Назначение: | Поместить в стек все регистры общего назначения |
Процессор: | 80186 80386 |
На самом деле 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 |
Команда: | IN приемник, источник |
Назначение: | Считать данные из порта |
Процессор: | 8086 |
Команда: | OUT приемник, источник |
Назначение: | Записать данные в порт |
Процессор: | 8086 |
Команда: | CWD |
Назначение: | Конвертирование слова в двойное слово |
Процессор: | 8086 |
Команда: | CDQ |
Назначение: | Конвертирование двойного слова в учетверенное |
Процессор: | 80386 |
Команда: | CBW |
Назначение: | Конвертирование байта в слово |
Процессор: | 8086 |
Команда: | CWDE |
Назначение: | Конвертирование слова в двойное слово |
Процессор: | 80386 |
Так же как и в случае с командами PUSHA/PUSHAD, пара команд CWD/CDQ — это одна команда с кодом 99h, и пара команд CBW/CWDE — одна команда с кодом 98h. Интерпретация этих команд зависит от того, в каком (16-битном или в 32-битном) сегменте они исполняются, и точно так же, если указать CDQ или CWDE в 16-битном сегменте, ассемблер поставит префикс изменения разрядности операнда. | ||
Команда: | MOWSX приемник, источник |
Назначение: | Пересылка с расширением знака |
Процессор: | 80386 |
Команда: | MOWZX приемник, источник |
Назначение: | Пересылка с расширением нулями |
Процессор: | 80386 |
Команда: | XLAT адрес XLATB |
Назначение: | Трансляция в соответствии с таблицей |
Процессор: | 8086 |
Команда: | LEA приемник, источник |
Назначение: | Вычисление эффективного адреса |
Процессор: | 8086 |
Команду 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 x86 после включения питания оказывается в так называемом режиме реальной адресации памяти, или просто реальном режиме. Большинство операционных систем сразу же переводят его в защищенный режим, позволяющий им обеспечивать многозадачность, распределение памяти и другие функции. Пользовательские программы в таких операционных системах часто работают еще в одном режиме, режиме V86, из которого им доступно все то же, что и из реального, кроме команд, относящихся к управлению защищенным режимом. Таким образом, эта глава описывает не только реальный режим, но и V86, то есть все то, что доступно программисту, если он не проектирует операционную систему или DPMI-сервер, в подавляющем большинстве случаев.
Процессоры 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);
Начиная с модификации процессора 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 предоставляет восемь регистров для хранения данных и пять вспомогательных регистров.
Регистры данных (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 — флаг антипереполнения — результат слишком маленький.
Значение RC | Способ округления |
0 | к ближайшему числу |
1 | к отрицательной бесконечности |
2 | к положительной бесконечности |
3 | к нулю |
Значение PC | Точность результатов |
0 | одинарная точность (32-битные числа) |
1 | зарезервировано |
2 | двойная точность (64-битные числа) |
3 | расширенная точность (80-битные числа) |
Расширение ММХ включает в себя восемь 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 не определено (кроме случая, если счетчик сдвига равен нулю, в котором ничего не происходит и флаги не изменяются).
Команда: | SHRD приемник, источник, счетчик |
Назначение: | Сдвиг повышенной точности вправо |
Команда: | SHLD приемник, источник, счетчик |
Назначение: | Сдвиг повышенной точности влево |
Процессор: | 80386 |
Команда: | ROR приемник, счетчик |
Назначение: | Циклический сдвиг вправо |
Команда: | ROL приемник, счетчик |
Назначение: | Циклический сдвиг влево |
Команда: | RCR приемник, счетчик |
Назначение: | Циклический сдвиг вправо через флаг переноса |
Команда: | RCL приемник, счетчик |
Назначение: | Циклический сдвиг влево через флаг переноса |
Процессор: | 8086 |
Команда: | 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 |
Команда: | SCAS приемник |
Назначение: | Сканирование строки |
Процессор: | 8086 |
Команда: | SCASB |
Назначение: | Сканирование строки байт |
Процессор: | 8086 |
Команда: | SCASW |
Назначение: | Сканирование строки слов |
Процессор: | 8086 |
Команда: | SCASD |
Назначение: | Сканирование строки двойных слов |
Процессор: | 80386 |
Команда: | LODS источник |
Назначение: | Чтение из строки |
Процессор: | 8086 |
Команда: | LODSB |
Назначение: | Чтение байта из строки |
Процессор: | 8086 |
Команда: | LODSW |
Назначение: | Чтение слова из строки |
Процессор: | 8086 |
Команда: | LODSD |
Назначение: | Чтение двойного слова из строки |
Процессор: | 80386 |
Команда: | STOS приемник |
Назначение: | Запись в строку |
Процессор: | 8086 |
Команда: | STOSB |
Назначение: | Запись байта в строку |
Процессор: | 8086 |
Команда: | STOSW |
Назначение: | Запись слова в строку |
Процессор: | 8086 |
Команда: | STOSD |
Назначение: | Запись двойного слова в строку |
Процессор: | 80386 |
Команда: | INS источник, DX |
Назначение: | Чтение строки из порта |
Процессор: | 80186 |
Команда: | INSB |
Назначение: | Чтение строки байт из порта |
Процессор: | 80186 |
Команда: | INSW |
Назначение: | Чтение строки слов из порта |
Процессор: | 80186 |
Команда: | INSD |
Назначение: | Чтение строки двойных слов из порта |
Процессор: | 80386 |
Команда: | OUTS DX, приемник |
Назначение: | Запись строки в порт |
Процессор: | 80186 |
Команда: | OUTSB |
Назначение: | Запись строки байт в порт |
Процессор: | 80186 |
Команда: | OUTSW |
Назначение: | Запись строки слов в порт |
Процессор: | 80186 |
Команда: | OUTSD |
Назначение: | Запись строки двойных слов в порт |
Процессор: | 80386 |
Числовой процессор может выполнять операции с семью разными типами данных, представленными в таблице 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-битная мантисса без первой цифры;
Кроме обычных чисел формат 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; неподдерживаемое число: все остальные ситуации. | ||
ММХ использует четыре новых типа данных:
учетверенное слово — простое 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 для байта со знаком), то результат считают равным этому максимальному значению. Если он меньше минимального значения — соответственно его полагают равным минимально допустимому значению. Например, при операциях с цветом насыщение позволяет ему превращаться в чисто белый при переполнении и в чисто черный при антипереполнении, в то время как обычная арифметика привела бы к нежелательной инверсии цвета.
Команда: | 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).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 |
Команда: | F2XMI |
Назначение: | Вычисление 2х-1 |
Процессор: | 8087 |
Команда: | FYL2X |
Назначение: | Вычисление у*log2(x) |
Процессор: | 8087 |
Команда: | FYL2XP1 |
Назначение: | Вычисление у*log2(x+1) |
Процессор: | 8087 |
Команда: | 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 |
Команда: | STI |
Назначение: | Разрешить прерывания |
Процессор: | 8086 |
Команда: | SALC |
Назначение: | Установить AL в соответствии с CF |
Процессор: | 8086 |
Команда: | LDS приемник, источник |
Назначение: | Загрузить адрес, используя DS |
Процессор: | 8086 |
Команда: | LES приемник, источник |
Назначение: | Загрузить адрес, используя ES |
Процессор: | 8086 |
Команда: | LFS приемник, источник |
Назначение: | Загрузить адрес, используя FS |
Процессор: | 80386 |
Команда: | LGS приемник, источник |
Назначение: | Загрузить адрес, используя GS |
Процессор: | 80386 |
Команда: | LSS приемник, источник |
Назначение: | Загрузить адрес, используя SS |
Процессор: | 8086 |
Второй операнд (источник) для всех этих команд — переменная в памяти размером в 32 или 48 бит (в зависимости от разрядности операндов). Первые 16 бит из этой переменной загружаются в соответствующий сегментный регистр (DS для LDS, ES для LES и т.д.), а следующие 16 или 32 — в регистр общего назначения, указанный в качестве первого операнда. В защищенном режиме значение, загружаемое в сегментный регистр, всегда должно быть правильным селектором сегмента (в реальном режиме любое число может использоваться как селектор).