Конструкторы микропроцессоров от i8080 до i80386 не смогли реализовать все математические функции на одном кристалле, так как это оказалось слишком сложным. Вместо этого они создали набор функций более низкого уровня, из которых можно программно построить любые математические функции. Вот так программист должен был вычислять, используя арифметические операции, функции синуса, экспоненты или логарифма: [math]sin(x)=x-\frac{\displaystyle x^{3}}{\displaystyle 3!}+\frac{\displaystyle x^{5}}{\displaystyle 5!}-...+(-1)^{n}\times \frac{\displaystyle x^{2n+1}}{\displaystyle (2n+1)!}[/math] [math]e^{x}=1+\frac{\displaystyle x}{\displaystyle 1!}+\frac{\displaystyle x^{2}}{\displaystyle 2!}+...+\frac{\displaystyle x^{n}}{\displaystyle n!}[/math] [math]ln(x)=(x-1)-\frac{\displaystyle (x-1)^{2}}{\displaystyle 2}+\frac{\displaystyle (x-1)^{3}}{\displaystyle 3}-...+(-1)^{n-1}\times\frac{\displaystyle (x-1)^{n}}{\displaystyle n}[/math]Для обработки чисел с плавающей запятой в 1980 году компания Intel выпустила специальную микросхему i8087 – математический сопроцессор. Сопроцессор i8087 работал с первыми 16-разрядными микропроцессорами Intel 8086 и 8088. Основной микропроцессор и сопроцессор работали во взаимодействии друг с другом. Когда основной микропроцессор считывал специальную команду Esc=1101.1XXX2 (код от 1101.10002=D816 до 1101.11112=DF16), управление передавалось сопроцессору, который выполнял следующий за этой командой машинный код – одну из 68 команд для вычисления логарифма, тригонометрических функций, возведения в степень и так далее. Получив команду на выполнение, сопроцессор запускал набор собственных команд, записанных в ПЗУ. Обычно выполнение внутренних команд сопроцессора сопряжено с вычислением в цикле, поэтому основной микропроцессор в это время простаивал, ожидая получения результата от микропроцессора. И все же, сопроцессор позволял ускорить вычисления, по сравнению с тем, если бы основному микропроцессору пришлось обрабатывать числа с плавающей запятой программным способом. Начиная с микропроцессора i80486 произошло объединение на одном кристалле математического сопроцессора с основным микропроцессором. Поэтому хватит вычислять значения синуса рядами, а воспользуемся командами, специально предназначенными для этого. Каждый FPU (Floating Point Execute Unit – блок обработки чисел с плавающей запятой) имеет собственный набор команд и средства для выполнения операций с плавающей запятой, такие как экспоненциальные, логарифмические и тригонометрические функции. Блок FPU содержит восемь 80-битовых регистров (от ST(0) до ST(7)) для обработки чисел с плавающей запятой, которые могут представлять числовые значения от 10-400 до 10400. Регистры данных блока FPU, в отличие от регистров общего назначения, не независимы, а организованы в стек, то есть операнды считываются в порядке, обратном их записи. Вычисления с плавающей точкой выполняются блоком FPU параллельно с работой основной программы, иногда даже быстрее вычислений, производимых блоком IEU. И программисты стали перекладывать основную тяжесть вычислений на блок FPU. Блок FPU поддерживает следующие типы данных: Типы данных, обрабатываемых блоком FPU ТипРезервирование памяти с помощьюРазмер (байт)Диапазон значенийКороткое вещественноеDD/REAL44От ±1,18×10-38 до ±3,4×1038Длинное вещественноеDQ/REAL88От ±2,23×10-308 до ±1,79×10308Расширенное вещественноеDT/REAL1010От ±3,37×10-4932 до ±1,18×104932Целое словоDW/SWORD2От -32768 до +32767Короткое целоеDD/SDWORD4 От -231 до +231-1Длинное целоеDQ/SQWORD8От -263 до +263-1Упакованное двоично- десятичное целоеDT/TBYTE10От -999999999999999999 до +999999999999999999Вещественные форматы IEEE, применяемые в микропроцессорах IntelТипРазмер (байт)Соответствие вещественному числу в C/C++Формат числа (в битах)знакпорядокмантиссаDD/REAL44короткое (float)1823DQ/REAL88длинное (long, double)11152DT/REAL1010расширенное (long double)11564Процессор передает числовые данные в блок FPU, который осуществляет необходимые вычисления и возвращает результат. Для ассемблирования команд FPU, если модель вашего микропроцессора младше i80486, необходимо добавлять параметр – .8087,.287,.387 Названия всех команд FPU начинаются с буквы F. Чтобы вам было легче запомнить назначение команд FPU, обратите внимание на следующую таблицу: Расшифровка аббревиатур в командах FPU Буква в командеОбозначениеПримерI после буквы FОперанд – целое число (Integer - целое)fild, fist, fiadd, fisub, ficompB после буквы FОперанд – упакованное двоично-десятичное число (packed Binary-coded decimal)fbld,fbstpP после буквы Fpartial - частичныйfptan, fprem, fprem1после буквы F название операцииОперанд – действительное числоfld, fst, fadd, fsubP в конце командывыборка операнда из стека (to pop - вытолкнуть)fstp, faddp, fsubpPP в конце командывыборка из стека двух операндовfcomppR в конце команды или перед PОбратный (Reverse - переставленный) порядок операндовfdivr, fsubrСпециальные численные значенияЗнак (79 бит)Порядок (78-64 биты)Мантисса (63-0 биты)Название числа0000...00000000000...00000000000+01000...00000000000...00000000000-0х000...00000000ххх...хххххххххххДенормализованные числа, используются для работы с очень маленькими числами от ±10-4932 до ±10-164450111...11111111000...00000000000+∞1111...11111111000...00000000000-∞х111...1111111110ххх...хххххххххНечисло типа SNAN (Signal Non A Number). Среди х есть единицы. FPU реагирует на появление SNAN возбуждением исключения недействительной операциих111...1111111111ххх...хххххххххНечисло типа QNAN (Quiet Non A Number). Среди х есть единицы1111...111111111100...0000000000Неопределенность (один из вариантов QNAN)Несмотря на большой диапазон вещественных значений, представимых в регистрах данных FPU, значения, например, -∞ и +∞ находятся за пределами этого диапазона. Для того, чтобы иметь возможность реагировать на некоторые вычислительные ситуации, которые могут возникнуть в результате математических операций в блоке FPU, предусмотрены специальные комбинации битов, называемые специальными численными значениями. Над специальными численными значениями можно выполнять некоторые операции. Программист может и сам кодировать специальные численные значения директивой DT (х – любое значение бита). Если в результате вычислений мантисса и порядок равны нулю, то, несмотря на скрытую единицу в целой части числа REAL4/8, все число полагается равным нулю. Любой конечный результат операции FPU, который из-за ограниченности поля порядка не может быть представлен в 23/52/64 разрядах для REAL4/8/10, соответственно вызывает особый случай некорректности представления числа. Программная модель FPUБлок FPU предоставляет для программирования восемь регистров для хранения данных (R0-R7) и пять вспомогательных регистров: регистр SWR (State Word Register регистр состояния), регистр CWR (Control Word Register регистр управления), регистр TW (Tag Word регистр атрибутов) и регистры FIP (FPU Instruction Pointer) и FDP (FPU Data Pointer). Регистры данных R0-R7Регистры данных не адресуются по именам, а рассматриваются в качестве стека, вершина которого называется ST(0). Если в какой-то момент вершина стека (поле TOP регистра SWR) указывает на регистр R5 и его считают ST(0), то после записи числа в R5, вершина стека будет указывать на регистр R4 и уже он будет ST(0), а R5 – ST(1) и так далее. Регистр тэгов TWРегистр тэгов хранит состояние содержимого каждого регистра данных (на каждый регистр данных отведено по 2 бита). Содержимое регистра данных может быть Tag FieldСодержимое00Число в формате с плавающей запятой (valid)01Нулевое значение10Специальное значение (SNAN, QNAN, бесконечность, денормализованное или не поддерживаемое число)11Пусто (empty)Регистры FIP и FDPРегистры FIP (FPU Instruction Pointer) и FDP (FPU Data Pointer) содержат адрес последней выполненной команды (за исключением команд FINT, FCLEX, FLDCW, FSTSW, FSTSWAX, FSTENV, FLDENV, FSAVE, FRSTORE и FWAIT) и адрес ее операнда соответственно, и используются в обработчиках исключений для анализа вызвавшей его команды. Регистр состояний RSWРегистр состояния фиксирует различные ошибки, хранит код условий для некоторых команд, определяет регистр – вершину стека и показывает состояние занятости блока FPU: БитНазваниеНазначение15FPU busyЗанятость FPU. Этот флаг существует для совместимости с i8087, и его значение всегда совпадает с ES.14,10-8Condition Code (С3,С2,С1,С0)Условные флаги. Употребляются так же, как и биты состояния в регистре EFLAGS, значения С0–С3 отражают результат выполнения предыдущей команды блока FPU и используются для условных переходов. Значения С0–С3 копируют в регистр EFLAGS: флаг С0 переходит в CF, C2→ PF, C3→ ZF, флаг C1 теряется.13-11Тop of Stack PointerЧисло от 0 до 7, показывающее, какой из регистров данных R0–R7 в настоящий момент является вершиной стека.7Error Summary StatusОбщий флаг ошибки. Если ES=1, произошло хотя бы одно немаскированное исключение.6Stack FaultОшибка стека. Если С1=1, произошло переполнение (команда пыталась писать в непустую позицию в стеке), если С1=0, произошло антипереполнение (команда пыталась считать число из пустой позиции в стеке).5precisionФлаг неточного результата – результат не может быть представлен точно.4UnderflowФлаг антипереполнения – результат слишком маленький.3overflowФлаг переполнения – результат слишком большой.2Zero DivideФлаг деления на ноль – выполнено деление на ноль.1Denormalized OperandФлаг денормализованного операнда – выполнена операция над денормализованным числом.0Invalid OperationФлаг недопустимой операции – произошла ошибка стека (SF=1) или выполнена недопустимая операция.Биты 0–5 отражают различные ошибочные ситуации, которые могут возникать при выполнении команд FPU. Они рассмотрены в описании управляющих регистров. Регистр управления RCWРегистр управления содержит слово управления блока FPU: БитыНазваниеНазначение12Infinity Controlуправление бесконечностью (поддерживается для совместимости с i8087 и i80287 – вне зависимости от значения этого бита +∞ больше чем -∞)11–10Rounding Controlуправление округлением9–8precision Controlуправление точностью5precision Maskмаска неточного результата4Underflow Maskмаска антипереполнения3overflow Maskмаска переполнения2Zero divide Maskмаска деления на ноль1Denormalized Operand Maskмаска денормализованного операнда0Invalid Operation Maskмаска недействительной операцииБиты 0–5 регистра CRW маскируют соответствующие исключения – если маскирующий бит установлен, исключения не происходит, а результат вызвавшей его команды определяется правилами для каждого исключения специально. С появлением Pentium MMX произошло расширение системы команд за счет включения 57 новых инструкций, разработанных для более эффективной работы с мультимедийными данными. Параллельное исполнение повторяющихся последовательностей команд целочисленной арифметики, часто встречающихся при работе мультимедиа-приложений, происходит в 64-битных регистрах MM0-MM7. Физически никаких новых регистров с введением технологии MMX не появилось, регистры MM0-MM7 – это мантиссы восьми регистров блока FPU от R0 до R7. При записи числа в регистр MMX оно оказывается в битах 63-0 соответствующего регистра FPU. А порядок (биты 78-64) и ее знаковый бит (бит 79) заполняются единицами. Запись числа в регистр FPU также приводит к изменению содержания соответствующего регистра MMX. Любая команда MMX, кроме EMMS, приводит к тому, что поле TOP регистра SR и весь регистр TW в FPU обнуляется. Команда EMMS заполняет регистр TW единицами. Нельзя одновременно пользоваться командами для работы с числами с плавающей запятой и командами MMX, если возникла такая необходимость – применяют команды FSAVE/FRSTOR при переходе от FPU к MMX. Для доступа к регистрам данных (R0-R7) FPU можно использовать команды для доступа к регистрам MM0-MM7. Пример работы с FPUНапишем программу с использованием команд FPU Код (ASM): .686P .model flat,stdcall includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\msvcrt.lib includelib c:\masm32\lib\kernel32.lib extern _imp__MessageBoxA@16:dword extern _imp__sprintf:dword extern _imp__ExitProcess@4:dword .data quotient dq 0 buffer db 80 dup (0) format db 'Quotient: %f',0 caption db 'Divide',0;заголовок .code start: finit push 3 fild dword ptr [esp] mov dword ptr [esp], 2 fild dword ptr [esp] add esp, 4 fdivp st(1),st(0) fstp qword ptr quotient push dword ptr quotient+4 push dword ptr quotient push offset format push offset buffer call _imp__sprintf add esp,4*4;балансируем стек после вызова sprintf push 0 push offset caption push offset buffer push 0 call _imp__MessageBoxA@16;выводим на экран push 0 call _imp__ExitProcess@4 end start Запускаем программу и любуемся на результат Команда fild dword ptr [esp] (Integer LOAD) – загружает целое число =3 из верхушки стека в ST(0). При загрузке числа блок FPU преобразует входное значение из формата знакового целого (16-, 32-, 64-битного) в формат с плавающей точкой. Затем на вершину стека помещается число 2, снова загрузка в ST(0), число 3 перемещается в регистр ST(1). Затем делим содержимое ST(1) на ST(0) командой fdivp st(1),st(0). Команда fstp qword ptr quotient копирует результат из ST(0) в ячейку памяти quotient, при этом выполняется обратное преобразование из внутреннего формата FPU в вещественный формат с округлением результата. Текущий режим округления – к ближайшему значению – установлен в результате выполнения команды finit (Floating-point unit INITIALIZE). Результат выводим на экран Система команд блока FPUКоманды блока FPU по выполняемым им функциям делят на шесть групп: передача данных арифметические сравнения трансцендентальные оперирующие константами команды управления Кодировка всех команд блока FPU начинается с 1101.1xxx (с 0D8h по 0DFh) (ESC1-ESC7 «ESC-коды»). Команды передачи данных Команда FLD (загрузка числа в FPU ”Floating point LOAD”)Синтаксис команды: FLD <SRC> Алгоритм работы: загрузки числа в регистр данных блока FPU. Возможные варианты команды: fild mem fbld mem fld mem fld st(i) Псевдокод команды: IF SRC =ST(i) _THEN temp←ST(i) ENDIF TOP←TOP-1 IF SRC is memory-operand _THEN ST(0)←ConvertToDoubleExtendedPrecisionFP(SRC) _ELSE ST(0)← temp END IF Применение: FILD SRC – загрузка в регистр FPU ST(0) целого числа из ячейки памяти SRC. Область памяти SRC должна быть 16-, 32-, 64-битной; FBLD SRC – загрузка в ST(0) десятичного упакованного числа из 80-битной области памяти SRC; FLD SRC – загрузка в ST(0) действительного числа из памяти. Область памяти SRC должна быть 32-, 64-, 80-битной; FLD ST(i) – загрузка в регистр ST(0) содержимого регистра ST(i). Все команды помещаются число в ST(0), увеличивая указатель стека на 1 (ST(0)→ST(1)) и уменьшая значение поля TOP в регистре TW на 1. Хотя используется одна и та же инструкция FLD SRC, но по типу имени операнда SRC ассемблер подбирает одну из четырех команд с разными опкодами (в кодах ясно угадывается адрес ячейки памяти 400174h, записанный в обратном порядке 74014000, и индекс регистра FPU), то же можно сказать и о команде FILD SRC. Командаhex для MOD=00 и R/M=101ОпкодESCOPAModOPBR/MадресFLD dword ptr [400174h]D905.74014000D9/0ESC001MOD000R/M400174FLD qword ptr [400174h]DD05.74014000DD/0ESC101MOD000R/M400174FLD tbyte ptr [400174h]DB2D.74014000DB/5ESC011MOD101R/M400174FLD ST(i)D9CiD9C0+iESC00111000ST(i)FILD word ptr [400174h]DF05.74014000DF/0ESC111MOD000R/M400174FILD dword ptr [400174h]DB05.74014000DB/0ESC011MOD000R/M400174FILD qword ptr [400174h]DF2D.74014000DF/5ESC111MOD101R/M400174FBLD tbyte ptr [400174h]DF25.74014000DF/4ESC111MOD100R/M400174Команда FLD ST(0) дублирует вершину стека. После нее регистры ST(0) и ST(1) имеют одинаковое значение. Командаhex для MOD=00 R/M=101ОпкодESCOPAModOPBR/MFLD m32fpD905D9/0ESC001MOD000R/MFLD m64fpDD05DD/0ESC101MOD000R/MFLD m80fpDB2DDB/5ESC011MOD101R/MFLD ST(i)D9CiD9C0+iESC00111000ST(i)FILD m16intDF05DF/0ESC111MOD000R/MFILD m32intDB05DB/0ESC011MOD000R/MFILD m64intDF2DDF/5ESC111MOD101R/MFBLD m80bcdDF25DF/4ESC111MOD100R/MMod=11 режим регистровой адресации. R/M=ST(i)Mod=00 R/M=101 режим прямой адресации, в следующем байте 32-битное смещение ячейки памяти
Команда FLD ST(3) помещает содержимое ST(3) в ST(0). Число, которое имело адрес ST(3), имеет теперь адрес ST(4). Но если специально не указывать номер регистра данных FPU, то по команде FLD загружаемое в FPU число попадает в ST(0), при этом числа, уже хранящиеся в других регистрах, смещаются на шаг от вершины стека. Стек FPU может хранить только 8 чисел – столько, сколько в нем регистров. Попытка загрузить в стек девятое число приведет к потере числа, далее всего отстоящего от вершины. Но и ST(0) при этом не воспримет то, что в него загружается, и будет содержать некое значение, которое с точки зрения FPU не может быть числом. Загрузим FPU числами 1, 2, 3, …, 9. Первым в FPU оказалось число 1.0. Оно заняло вершину стека, то есть регистр ST(0). Далее в ST(0) было загружено число 2.0, а число 1.0 спустилось ниже – в регистр ST(1). Затем в ST(0) побывали числа 3.0, 4.0, 5.0, 6.0, 7.0, 8.0. Число 1.0, попавшее в ST(0) первым, спускалось все ниже и оказалось, наконец, в регистре ST(7), когда в ST(0) было число 8.0. Но, при попытке поместить в ST(0) девятое число, случилась авария: число 1.0, загруженное первым, покинуло стек, а на его вершине оказалось неверное значение, которое в регистре тэгов помечено атрибутом NAN (Non A Number нечисло). Кроме «нечисел» в стеке могут быть нормальные числа, помеченные атрибутом valid (правильный). Таковы все числа кроме первого. У регистров ST(0)-ST(7) может быть еще один атрибут empty (пустой). Так блок FPU помечает регистр, в который можно загрузить число. Если регистр занят, то его нужно перед использованием освободить. Делается это командой ffree (FREE Floating-point register). Чтобы, например, освободить третий регистр, нужна команда ffree ST(3). Есть еще одна команда finit, которая разом освобождает сразу все регистры и чаще всего используется для приведения стека данных в «исходное» состояние. Команда FST (извлечение числа из FPU в память ”STORE Floating point value”)Синтаксис команды: FST <DEST> Алгоритм работы: сохранение числа из регистра данных блока FPU в ячейку памяти. Возможные варианты команды: fist mem fistp mem fistpp mem fisttp mem fbstp mem fstp mem Псевдокод команды: IF Команда = FSTP _THEN DEST←ST(0) ELSE IF Команда = FBSTP _THEN DEST←Преобразовать_в_BCD(ST(0)) ELSE IF Команда = FISTP или FIST _THEN DEST←Преобразовать_в_целое(ST(0)) ENDIF; IF Команда = FISTP или FSTP или FBSTP _THEN Вытолкнуть_содержимое_регистра_из_стека ENDIF; Применение: команда FST извлекает число из вершины стека и записывает его в ячейку памяти DEST. Область памяти DEST должна быть 16-, 32-, 64-, 80-битной. Команда FSTP выполняет операцию записи данных из FPU в память и извлекает число из стека. Сравним команды FLD/FST/FSTP/FILD/FIST/FISTP. Отличие всего в одном бите (FLD d=0/FST d=1) КомандаhexОпкодESCOPAMODOPBR/MДействиеFST m32fpD915D9/2ESC001000dp101d=1 p=0FSTP m32fpD91DD9/3ESC001000dp101d=1 p=1FLD m32fpD905D9/0ESC001000dp101d=0 p=0FST m64fpDD15DD/2ESC101000dp101d=1 p=0FSTP m64fpDD1DDD/3ESC101000dp101d=1 p=1FLD m64fpDD05DD/0ESC101000dp101d=0 p=0FST ST(i)DDD0-7DDD0+iESC101110dpST(i)d=1 p=0FSTP ST(i)DDD8-FDDD8+iESC101110dpST(i)d=1 p=1FSTP8 ST(i)DFD0-7DFD0+iESC11111010ST(i)FSTP9 ST(i)DFD8-FDFD8+iESC11111011ST(i)FLD ST(i)D9C0-7D9C0+iESC001110dpST(i)d=0 p=0FIST m16intDF15DF/2ESC111000dp101d=1 p=0FISTP m16intDF1DDF/3ESC111000dp101d=1 p=1FILD m16intDF05DF/0ESC111000dp101d=0 p=0FIST m32intDB15DB/2ESC011000dp101d=1 p=0FISTP m32intDB1DDB/3ESC011000dp101d=1 p=1FILD m32intDB05DB/0ESC011000d0101d=0FST m80fpESCFSTP m80fpDB3DDB/7ESC011001d1101d=1FLD m80fpDB2DDB/5ESC011001d1101d=0FIST m64intESCFISTP m64intDF3DDF/7ESC011001d1101d=1FILD m64intDF2DDF/5ESC111001d1101d=0FISTTP m64intDD0DDD/1ESC10100001101Store Integer with Truncation and PopFISTTP m32intDB0DDB/1ESC01100001101FISTTP m16intDF0DDF/1ESC11100001101FBST m80bcdESCFBSTP m80bcdDF35DF/6ESC011001d0101d=1FBLD m80bcdDF25DF/4ESC111001d0101d=0Команда FXCH (обмен значениями в регистрах FPU = ”EXCHGE register contents”)Синтаксис команды: FXCH <SRC> Семантика команды: команда обмена значения в регистре стека ST(0) со значением регистра ST(i) Возможные варианты команды: fxch st(i) fxch Псевдокод команды: IF количество_операндов=1 THEN _temp←ST(0) _ST(0)←SRC _SRC←temp ELSE _temp←ST(0) _ST(0)←ST(1) _ST(1)←temp ENDIF командаhexESCOPAModOPBR/MFXCH ST(i)D9C8-FESC00111001ST(i)FXCH4 ST(i)DDC8-FESC10111001ST(i)FXCH7 ST(i)DFC8-FESC11111001ST(i)FXCHD9C9ESC00111001ST(1)Команда FXCH меняет местами содержимое ST(0) с содержимым ST(i). Если операнд не указан, то обмениваются ST(0) и ST(1). Арифметические команды Команда FADD (сложение значений в регистрах FPU ” Floating point ADDITION”)Синтаксис команды: FADD <DEST>,<SRC> Семантика команды: команда сложения вещественных чисел. Возможные варианты команды: fadd st(0),st(i) fadd st(i),st(0) fadd mem faddp st(i),st(0) fiaddp mem Псевдокод команды: IF Команда=FIADD _THEN DEST ← DEST + ConvertToDoubleExtendedPrecisionFP(SRC) ELSE; операнд в SRC представлен в виде числа с плавающей запятой _DEST ← DEST + SRC ENDIF IF Команда=FADDP _THEN Вытолкнуть_содержимое_регистра_из_стека ENDIF 9-8 биты регистра управления RCW – биты PC (Precision Control) определяют точность результатов команд FADD, FSUB, FSUBR, FMUL, FDIV, FDIVR и FSQRT . PCТочность результатов00одинарная точность (32-битные числа)01зарезервировано10двойная точность (64-битные числа)11расширенная точность (80-битные числа)Кодировка FADDOP опкод команды, может состоять из двух частей OPA и OPB MF = Memory Format 0032-разрядное вещественное0132-разрядное целое1064-разрядное вещественное1116-разрядное целоеP = Pop 0Не выталкивает аргумент из стека1Выталкивает из стека после выполнения командыd = Направление (direction) 0ST(0)1ST(i), памятьПримерhexОпкодESCOPAModOPBR/MДействиеFADDPDEC1DEC1ESCd P 011000ST(1)d=1 P=1 ST(1) ← ST(0) + ST(1), извлечь из стекаFADD ST(0),ST(i)D8C0-7D8C0+iESCd P 011000ST(i)d=0 P=0 ST(0) ← ST(0) + ST(i)FADD ST(i),ST(0)DCC0-7DCC0+iESCd P 011000ST(i)d=1 P=0 ST(i) ← ST(0) + ST(i)FADDP ST(i),ST(0)DEC0-7DEC0+iESCd P 011000ST(i)d=1 P=1 ST(i) ← ST0 + ST(i), извлечь из стекаFADD m32fpD800-7 D840-7 D880-7D8/0ESCMF 0MOD=00 MOD=01 MOD=10000R/MST(0) ← ST(0) + [m32fp]FADD m64fpDC00-7 DC40-7 DC80-7DC/0ESCMF 0MOD=00 MOD=01 MOD=10000R/MST(0) ← ST(0) + [m64fp]FIADD m32intDA00-7 DA40-7 DA80-7DA/0ESCMF 0MOD=00 MOD=01 MOD=10000R/MST(0) ← ST(0) + [m32int]FIADD m16intDE00-7 DE40-7 DE80-7DE/0ESCMF 0MOD=00 MOD=01 MOD=10000R/MST(0) ← ST(0) + [m16int]Команда FSUB (вычитание значений в регистрах FPU ”Floating point SUBTRACT”)Синтаксис команды: FSUB <DEST>,<SRC> Семантика команды: команда вычитания вещественных чисел. Возможные варианты команды: fsub fsub st(0),st(i) fsub st(i),st(0) fsub mem fisub st(i),st(0) fsubr fsubr st(0),st(i) fsubr st(i),st(0) fsubr mem fsubrp st(i),st(0) fisubrp mem Псевдокод команды: IF Команда=FISUB _THEN DEST ← DEST - ConvertToDoubleExtendedPrecisionFP(SRC) ELSE IF Команда=FSUB ; операнд в SRC представлен в виде числа с плавающей запятой _DEST ← DEST - SRC ELSE IF Команда=FISUBR _DEST ←ConvertToDoubleExtendedPrecisionFP(SRC) - DEST ELSE IF Команда=FSUBR ; операнд в SRC представлен в виде числа с плавающей запятой _DEST ← SRC - DEST ENDIF IF Команда=FSUBP или FSUBRP _THEN Вытолкнуть_содержимое_регистра_из_стека ENDIF Кодировка FSUB [math]Reverse = d \oplus R[/math] ОперацияhexОпкодESCOPAModOPBR/MДействиеFSUB m32fpD820-7 D860-7 D8A0-7D8/4ESCMF 0MOD=00 MOD=01 MOD=1010 RR/MR=0 ST(0) ← ST(0) - [m32fp]FSUBR m32fpD828-F D868-F D8A8-FD8/5ESCMF 0MOD=00 MOD=01 MOD=1010 RR/MR=1 ST(0) ← [m32fp] - ST(0)FSUB m64fpDC20-7 DC60-7 DCA0-7DC/4ESCMF 0MOD=00 MOD=01 MOD=1010 RR/MR=0 ST(0) ← ST(0) -[m64fp]FSUBR m64fpDC28-F DC68-F DCA8-FDC/5ESCMF 0MOD=00 MOD=01 MOD=1010 RR/MR=1 ST(0) ← [m64fp] -ST(0)FSUB ST(0),ST(i)D8E0-7D8E0+iESCd P 01110 RST(i)d=0 P=0 R=0 ST(0) ← ST(0) -ST(i)FSUB ST(i),ST(0)DCE8-FDCE8+iESCd P 01110 RST(i)d=1 P=0 R=1 ST(i) ← ST(i) -ST(0)FSUBP ST(i),ST(0)DEE8-FDEE8+iESCd P 01110 RST(i)d=1 P=1 R=1 ST(i) ← ST(i) -ST(0), извлечь из стекаFSUBPDEE9DEE9ESCd P 01110 RST(1)d=1 P=1 R=1 ST(1) ← ST(0) -ST(1), извлечь из стекаFISUB m32intDA20-7 DA60-7 DAA0-7DA/4ESCMF 0MOD=00 MOD=01 MOD=1010 RR/MR=0 ST(0) ← ST(0) - [m32int]FISUBR m32intDA28-F DA68-F DAA8-FDA/5ESCMF 0MOD=00 MOD=01 MOD=1010 RR/MR=1 ST(0) ← [m32int]-ST(0)FISUB m16intDE20-7 DE60-7 DEA0-7DE/4ESCMF 0MOD=00 MOD=01 MOD=1010 RR/MR=0 ST(0) ← ST(0) -[m16int]FISUBR m16intDE28-F DE68-F DEA8-FDE/5ESCMF 0MOD=00 MOD=01 MOD=1010 RR/MR=1 ST(0) ← [m16int]-ST(0)FSUBR ST(0),ST(i)D8E0-7D8E0+iESCd P 01110 RST(i)d=0 P=0 R=0 ST(0) ← ST(i) -ST(0)FSUBR ST(i),ST(0)DCE0-7DCE0+iESCd P 01110 RST(i)d=1 P=0 R=0 ST(i) ← ST(0) -ST(i)FSUBRP ST(i),ST(0)DEE0-7DEE0+iESCd P 01110 RST(i)d=1 P=1 R=0 ST(i) ← ST(0) -ST(i), извлечь из стекаFSUBRPDEE1DEE1ESCd P 01110 RST(1)d=1 P=1 R=1 ST(1)← ST(1) -ST(0), извлечь из стекакоманда FSUB mem это ST(0) ← ST(0) - [mem] если ST(0)=0 тогда ST(0) = -[mem] получаем эквивалент команды, которой нет FCHS mem Результаты действия fsubp, fsubrp и fsub st(0),st(1) эквивалентны. Команды FMUL (умножение значений в регистрах FPU ”Floating point MULTIPLY”)Синтаксис команды: FMUL <DEST>,<SRC> Семантика команды: команда умножения вещественных чисел Возможные варианты команды: fmul fmul st(i),st(0) fmul st(0),st(i) fmul mem fmulp fimul mem Псевдокод команды: IF Команда=FIMUL _THEN DEST ← DEST × ConvertToDoubleExtendedPrecisionFP(SRC) ELSE; операнд в SRC представлен в виде числа с плавающей запятой _DEST ← DEST × SRC ENDIF IF Команда=FMULP _THEN Вытолкнуть_содержимое_регистра_из_стека ENDIF Кодировка FMULОперацияhexОпкодESCOPAModOPBR/MДействиеFMUL m32fpD808-F D848-F D888-FD8/1ESCMF 0MOD=00 MOD=01 MOD=10001R/MST(0)← [m32fp]× ST(0)FMUL m64fpDC08-F DC48-F DC88-FDC/1ESCMF 0MOD=00 MOD=01 MOD=10001R/MST(0)← [m64fp] × ST(0)FMUL ST(0),ST(i)D8C8-FD8C8+iESCd P 011001ST(i)d=0 P=0 ST(0)← ST(i) × ST(0)FMUL ST(i),ST(0)DCC8-FDCC8+iESCd P 011001ST(i)d=1 P=0 ST(i)← ST(i) × ST(0)FMULP ST(i),ST(0)DEC8-FDEC8+iESCd P 011001ST(i)d=1 P=1 ST(i)← ST(i) × ST(0), извлечь из стекаFMULPDEC9DEC9ESCd P 011001ST(1)d=1 P=1 ST(1)← ST(1) × ST(0), извлечь из стекаFIMUL m32intDA08-F DA48-F DA88-FDA/1ESCMF 0MOD=00 MOD=01 MOD=10001R/MST(0)← [m32int] × ST(0)FIMUL m16intDE08-F DE4-8-F DE88-FDE/1ESCMF 0MOD=00 MOD=01 MOD=10001R/MST(0)← [m16int] × ST(0)Операция деления в FPU происходит дольше, чем операция умножения, поэтому старайтесь заменять деление на константу на умножение на (1/константа). Варианты возведения числа X в квадрат ST(0)← X2 Код (ASM): fld x fld st(0) fmul Код (ASM): fld x fmul x Код (ASM): fld x fmul st(0),st(0) Команды FDIV (деление значений в регистрах FPU ”Floating point DIVIDE”)Синтаксис команды: FDIV <DEST>,<SRC> Семантика команды: команда деления вещественных чисел. Возможные варианты команды: fdiv mem fdiv st(0),st(i) fdiv st(i),st(0) fdivp st(i),st(0) fdivp fidiv mem Псевдокод команды: IF (Команда=FIDIV или FDIV) и SRC=0 _THEN #Z ELSE IF (Команда=FIDIVR или FDIVR) и DEST=0 _THEN #Z ELSE IF Команда=FIDIV _THEN DEST ← ConvertToDoubleExtendedPrecisionFP(SRC)/DEST ELSE IF Команда=FDIV ; операнд в DEST представлен в виде числа с плавающей запятой _DEST ← SRC/ DEST ENDIF IF Команда=FDIVP или FDIVRP _THEN Вытолкнуть_содержимое_регистра_из_стека ENDIF Кодировка FDIVОперацияhexОпкодESCOPAModOPBR/MДействиеFDIV m32fpD830-7 D870-7 D8B0-7D8/6ESCMF 0MOD=00 MOD=01 MOD=1011 RR/MR=0 ST(0) ← ST(0) / [m32fp]FDIV m64fpDC30-7 DC70-7 DCB0-7DC/6ESCMF 0MOD=00 MOD=01 MOD=1011 RR/MR=0 ST(0) ← ST(0) /[m64fp]FDIV ST(0),ST(i)D8F0-7D8F0+iESCd P 01111 RST(i)d=0 P=0 R=0 ST(0) ← ST(0) /ST(i)FDIV ST(i),ST(0)DCF8-FDCF8+iESCd P 01111 RST(i)d=1 P=0 R=1 ST(i) ← ST(i) /ST(0)FDIVP ST(i),ST(0)DEF8-FDEF8+iESCd P 01111 RST(i)d=1 P=1 R=1 ST(i) ← ST(i) /ST(0), извлечь из стекаFDIVPDEF9DEF9ESCd P 01111 RST(1)d=1 P=1 R=1 ST(1) ← ST(1) /ST(0), извлечь из стекаFIDIV m32intDA30-7 DA70-7 DAB0-7DA/6ESCMF 0MOD=00 MOD=01 MOD=1011 RR/MR=0 ST(0) ← ST(0) / [m32int]FIDIV m16intDE30-7 DE70-7 DEB0-7DE/6ESCMF 0MOD=00 MOD=01 MOD=1011 RR/MR=0 ST(0) ← ST(0) /[m16int]FDIVR m32fpD838-F D878-F D8B8-FD8/7ESCMF 0MOD=00 MOD=01 MOD=1011 RR/MR=1 ST(0) ← [m32fp] / ST(0)FDIVR m64fpDC38-F DC78-F DCB8-FDC/7ESCMF 0MOD=00 MOD=01 MOD=1011 RR/MR=1 ST(0) ← [m64fp] /ST(0)FDIVR ST(0),ST(i)D8F8-FD8F8+iESCd P 01111 RST(i)d=0 P=0 R=1 ST(0) ← ST(i) /ST(0)FDIVR ST(i),ST(0)DCF0-7DCF0+iESCd P 01111 RST(i)d=1 P=0 R=0 ST(i) ← ST(0) /ST(i)FDIVRP ST(i),ST(0)DEF0-7DEF0+iESCd P 01111 RST(i)d=1 P=1 R=0 ST(i) ← ST(0) /ST(i), извлечь из стекаFDIVRPDEF1DEF1ESCd P 01111 RST(1)d=1 P=1 R=0 ST(1) ← ST(0) /ST(1), извлечь из стекаFIDIVR m32intDA38-F DA78-F DAB8-FDA/7ESCMF 0MOD=00 MOD=01 MOD=1011 RR/MR=1 ST(0) ← [m32int]/ST(0)FIDIVR m16intDE38-F DE78-F DEB8-FDE/7ESCMF 0MOD=00 MOD=01 MOD=1011 RR/MR=1 ST(0) ← [m16int]/ST(0)
Команда FPREM (вычисления частичного остатка ”Partial REMAINDER”)Синтаксис команды: FPREM Семантика команды: команда вычисления частичного остатка. Remainder ← ST(0) - (Q×ST(1)) Псевдокод команды: D ← exponent(ST(0)) - exponent(ST(1)) IF D<64 THEN _Q ← Integer(TruncateTowardZero(ST(0)/ST(1))) _ST(0) ← ST(0) - (ST(1)×Q) _C2←0 _C0,C3,C1←LeastSignificantBits(Q); (*Q2Q1Q0 *) ELSE _C2 ← 1 _N ← число между 32 и 63 _QQ ← Integer(TruncateTowardZero(ST(0)/ST(1))/2D-N) _ST(0)←ST(0) -(ST(1)×QQ×2D-N) ENDIF Эта команда за один раз уменьшает содержимое вершины стека максимум на 264. Команда выполняет нахождение истинного остатка от деления ST(0) на ST(1) и требует очень много времени для уменьшения большого числа по очень маленькому основанию. Если функция не завершена, команда FPREM устанавливает флаг C2 равным 1, а когда завершает вычисление остатка, то устанавливает другие три флага C3, C1 и C0 равными трем младшим битам частного. Когда команда FPREM используется в простых тригонометрических функциях для ограничения величины угла, это оказывается необходимым для определения октанта первоначального угла. Код (ASM): ;контроль выполнения частичного остатка (c2=pf=1) a1: fprem1 fstsw ax sahf jp a1 Команда FPREM1Синтаксис команды: FPREM1 Семантика команды: команда вычисления частичного остатка. Remainder← ST(0) -(Q× ST(1)) Псевдокод команды: D ← exponent(ST(0))-exponent(ST(1)) IF D<64 THEN _Q←Integer(RoundTowardNearestInteger(ST(0)/ST(1))) _ST(0) ← ST(0) - (ST(1)×Q) _C2←0 _C0,C3,C1 ← LeastSignificantBits(Q); (* _Q2Q1Q0 *) ELSE _C2← 1 _N ←число между 32 и 63 _QQ← Integer(TruncateTowardZero(ST(0)/ST(1))/2D-N) _ST(0) ← ST(0) - (ST(1) × QQ × 2D-N) ENDIF Также как и команда FPREM, команда FPREM1 выполняет деление ST(0) на ST(1) и помещает остаток от деления в ST(0). Деление осуществляется при помощи последовательных вычитаний ST(1) из ST(0), но за один раз выполняется не более 64 таких вычитаний. Команда FRNDINT (округления вещественного числа до целого ”Floating point ROUND to INTEGER”)Синтаксис команды: FRNDINT Семантика команды: команда округления до целого. Псевдокод команды: ST(0)← RoundToIntegerValue(ST(0)) Эта команда округляет текущее содержимое вершины стека до целого числа. Биты RC (Rounding Control) в регистре управления (RCW) определяют способ округления результатов команд блока FPU до заданной точности. RCСпособ округления00к ближайшему числу near01к -∞down10к +∞up11к нулюchop/zero Код (ASM): ;пример управления округлением fstcw ax and eax,1111xx1111111111b;11-10 bits rc or eax,xx0000000000b push eax fldcw word ptr [esp] pop eax frndint Команда FSQRT (извлечения квадратного корня ”Floating point SQUARE ROOT”)Синтаксис команды: FSQRT Алгоритм работы: извлечения квадратного корня. Псевдокод команды: ST(0) ← [math]\sqrt{ST(0)}[/math] ST(0) не должно быть отрицательно. Пример, найдем корни квадратного уравнения, заданного формулой: [math]a\cdot x^{2}+b\cdot x + c=0[/math] Из школьного курса математики известно, что решением этого уравнения является [math]x_{1,2}=\frac{- b\pm \sqrt{b^{2}-4\cdot a\cdot c}}{2\cdot a}[/math]. Программа для решения этого уравнения: Код (ASM): .data A dd ? ;место под коэффициенты B dd ? C1 dd ? STATUS DW ? ROOT1 dd ? ;место под 1-ый корень уравнения ROOT2 dd ?;место под 2-ой корень уравнения .code FNINIT ;инициализируем блок FPU FLD B ;загрузить число в регистр стека ST(0)<-B FMUL ST,ST ;перемножить числа ST(0)=B*B FLD A ;загрузить число в регистр стека, ST(0)=A FADD ST,ST FADD ST,ST; умножить число в ST(0) на 4.0, ST(0)=4a FMUL C1 ;умножить число в ST(0) на C, ST(0)=4ac FSUBRP ST(1),ST ; ST(0)=b*b - 4ac ;проверяем дискриминант на отрицательность FSQRT ;извлекаем квадратный корень в ST(0) FNSTSW STATUS;записываем в память управляющий регистр FPU MOV AH,BYTE PTR STATUS; Читаем в AH SAHF; помещаем содержимое регистра AH в регистр флагов JC IMAGINARY;если корни мнимые переходим на сообщение об ошибке FLD ST(0) ; ST(0) = ST(1) FCHS ;ST(0)=-SQRT(D) FSUB B;ST(0)=-B-SQRT(D) FLD A FADD ST(0),ST(0);ST(0)=2a FLD ST;ST(0)=ST(1)=2a FDIVP ST(2),ST ;ST(0)= (-B-SQRT(D))/2A FXCH FST ROOT1;сохраняем в памяти 1-ый результат из ST(0) FXCH FSUB B;ST(0)= -B+SQRT(D) FDIV ST,ST(1);ST(0)=(-B+SQRT(D))/2A FSTP ROOT2 ;сохраняем в памяти 2-ой результат Возможна проблема с потерей точности Еще раз вернемся к нашей задаче. Решением квадратного уравнения является [math]x_{1,2}=\frac{- b\pm \sqrt{b^{2}-4ac}}{2\cdot a}[/math] Пусть [math]b[/math] много больше [math]4ac[/math], тогда [math]\sqrt{b^{2} - 4ac}[/math] приблизительно равно [math]b[/math]. Таким образом, можно получить, что [math]-b+b=0[/math], значит [math]x_{1}=0[/math], что не является решением задачи. Преобразуем выражение для [math]x_{1}[/math]: [math]x_{1}=\frac{-b+\sqrt{D}}{2a}=\frac{-b+\sqrt{D}}{2a}\frac{b+\sqrt{D}}{b+\sqrt{D}}=\frac{D-b^{2}}{2a(b+\sqrt{D})}=\frac{b^{2}-4ac-b^{2}}{2a(b+\sqrt{D})}=-\frac{2c}{b+\sqrt{D}}[/math] и при [math]b[/math], намного большем [math]4ac[/math] получаем, что [math]x_{1}[/math] стремится к [math]-\frac{2c}{b}[/math]. Команды масштабирования (Возведение числа 2 в произвольную степень) Возведение числа 2 в целую степень. Команда FSCALE (Масштабирование числа ”Floating point SCALE ST(0) by ST(1)”) Синтаксис команды: FSCALE Семантика команды: команда для возведения 2 в степень, равную целому числу, находящемуся в ST(1) и умножение на содержимое ST(0). Результат в ST(0). Псевдокод команды: ST(0)← ST(0)×2ST(1) Возведение числа 2 в дробную степень. Команда F2XM1 (вычисление 2X -1”Floating point compute 2X-1”) Синтаксис команды: F2XM1 Семантика команды: команда вычисления 2 в степени X минус 1. Псевдокод команды: ST(0)← 2ST(1)-1 Эта функция выполняет возведение в степень; она возводит число 2 в степень, указанную в ST(0). Исходное число должно находиться в диапазоне 0 ≤ ST(0) ≤ 0,5 и, чтобы возвести в степень большую 0,5, эту команду нужно использовать вместе с командой FSCALE. С помощью команд FLD, загружающих специальные константы, программа может возвести в степень и числа отличные от 2, для этого можно воспользоваться формулами: 2x = (2x-1)+1 = F2XM1(x)+1 eX=2X·log2e= 1+F2XM1(x·log2(e)) = 1+ F2XM1(x·FLDL2E) 10X=2X·log210= 1+F2XM1(x·log2(10)) = 1+ F2XM1(x·FLDL2T) XY=2Y·log2X= 1+F2XM1(y·log2(x)) = 1+ F2XM1(FYL2X(y,x)) Команда FXTRACT (выделения порядка и значащей части ”EXTRACT exponent and significand”)Синтаксис команды: FXTRACT Семантика команды: выделения порядка и значащей части вещественного числа. Псевдокод команды: temp← дробная_часть(ST(0)) ST(0)←показательная_степень_числа_2ST(0) TOP← TOP-1 ST(0)← temp Эта команда разбивает текущую вершину стека на компоненты, ее операндом является вершина стека. Значение показателя степени замещает содержимое вершины стека, а затем дробная часть аргумента помещается в стек и становится новой вершиной стека. Действия команды FXTRACT обратные действиям команды FSCALE. Если в вершине стека находится некоторое число, то выполнение последовательно команд FXTRACT и FSCALE оставляет в вершине стека то же число. Но команда FSCALE не удаляет из стека показатель степени, так что теперь в стеке дополнительно окажется еще одно число. Команда FABS (вычисления абсолютного значения ”Floating point ABSOLUTE value”)Синтаксис команды: FABS Семантика команды: вычисления абсолютного значения вещественных чисел. Псевдокод команды: Знаковый_бит(ST(0))← 0 Эта команда сбрасывает знаковый разряд числа в ST(0) (что соответствует положительному значению). Команда FCHS (изменения знака ”Floating point CHANGE Sign”)Синтаксис команды: FCHS Семантика команды: изменения знака вещественного числа. Псевдокод команды: Знаковый_бит(ST(0))← NOT (Знаковый_бит(ST(0))) Команда изменяет знаковый разряд числа в ST(0) на противоположный. Кодировка трансцендентальных и прочих функций ОперацияОпкодESCOPAModOPBДействиеFCHSD9E0ESC0011110.0000ST(0)← -1× ST(0)FABSD9E1ESC0011110.0001ST(0)← абсолютное значение ST(0)F2XM1D9F0ESC0011111.0000ST(0)←(2ST(1)-1)FYL2XD9F1ESC0011111.0001ST(0)← ST(1) × log2 (ST(0))FPTAND9F2ESC0011111.0010ST(1)← tg(ST(0)) ST(0)← 1.0FPATAND9F3ESC0011111.0011ST(0)← Arctg(ST(1)/ST(0))FXTRACTD9F4ESC0011111.0100FPREM1D9F5ESC0011111.0101FPREMD9F8ESC0011111.1000FYL2XP1D9F9ESC0011111.1001ST(0)← ST(1)× log2(ST(0)+1)FSQRTD9FAESC0011111.1010ST((0)← [math]\sqrt{ST(0)}[/math]FSINCOSD9FBESC0011111.1011ST(0)← Sin(ST(0)) ST(1)← Cos(ST(0))FRNDINTD9FCESC0011111.1100FSCALED9FDESC0011111.1101ST(0)← ST(0)× 2ST(1)FSIND9FEESC0011111.1110ST(0)← Sin(ST(0))FCOSD9FFESC0011111.1111ST(0)← Cos(ST(0))Команды сравненияОперация сравнения чисел FPU отличается от целочисленной операции сравнения. Это отличие в большем количестве ситуаций и возможных результатов сравнения двух чисел. Числа могут быть: сравнимы несравнимы логически сравнимы В первой группе сравниваемые числа могут быть больше, меньше, равны и так далее. Во второй группе операнды не сравнимы. На пример, пытаются сравнить число и не число, число и денормализованное число и так далее. В третью группу входят ситуации второй группы, но логически переосмысленные. Например, сравнивая число с денормализованным числом, представляют его как сравнение с нулём, или как сравнение числа и бесконечности. Как видите третья группа – условна и зависит от постановки задачи, от её особенностей, и того, что имеет смысл, а что нет. Всё это вызывает достаточно значительные проблемы. Если вы устанавливаете исключения на все случаи, то есть исключаете ситуацию появления «ненормализованных» чисел – то ваш код можно будет реализовывать как обычно, не обращая внимания на ситуации с образованием ненормализованных результатов. Иначе, потребуется дополнительный анализ после сравнения. При этом заметьте, что в вашей задаче может иметь смысл денормализованный операнд, но не имеет смысла бесконечность, или наоборот. Команды FCOM (FCOMP, FCOMPP, FICOM, FICOMP) (сравнение вещественных чисел ”COMPARE Floating point values”)Синтаксис команды: FCOM <SRC> Семантика команды: команда сравнения вещественных чисел. Возможные варианты команды: fcom mem fcom st(i) fcomp mem fcomp st(i) fcompp mem fcompp st(i) ficom mem ficom st(i) ficomp mem ficomp st(i) Псевдокод команды: CASE (relation of operands) OF __ST(0)>SRC: C3,C2,C0←000 __ST(0)<SRC: C3,C2,C0←001 __ST(0)=SRC: C3,C2,C0←100 __Unordered: C3,C2,C0←111 ENDCASE В команде FCOM SRC всегда участвует вершина стека ST(0) и явно указанный регистр или операнд в памяти SRC. Команда FCOM SRC выполняет сравнение ST(0) с операндом SRC и устанавливает или сбрасывает биты состояния C3 и C0. При этом указатель стека TOP не модифицируется, если это не команда FCOMP. Особой командой сравнения является FCOMPP, которая не имеет операндов. Она всегда сравнивает два верхних элемента стека ST(0) и ST(1), после сравнения они оба исчезают из стека. Команды FICOM/FICOMP сравнивают содержимое регистра ST(0) и 16- или 32-битной переменной SRC, считается что в SRC содержится целое число. В остальном команды FICOM/FICOMP эквивалентны командам FCOM/FCOMP. Флаги состояния C3, C2 и C0 в FPU расположены на тех же местах, что и флаги нуля (ZF), паритета (PF) и переноса (CF) регистра EFLAGS. Регистр состояния SWRBC3TOPC2C1C0регистр EFLAGSSFZF-AF-PF-CFбиты76543210Для опроса флагов состояния, программа считывает содержимое SWR и записывает его в память командой FSTSW (FNSTSW), которая сохраняет содержимое регистра SWR в 16-битной переменной, или регистре AX. При помощи команды SAHF содержимое флагов C3 и C0 заносится в регистр флагов микропроцессора, правда при этом флаг C1 попадает на неиспользуемый в EFLAGS 1-бит и его значение теряется. Вместо SAHF можно также использовать команду POPF. Результат выполнения FCOM SRC C0/CFC3/ZFC2/PFЗначение000ST(0) > SRC100ST(0) < SRC010ST = SRC111ST(0) и SRC не могут быть сравненыОперацияhexопкодESCOPAModOPBR/MДействиеFCOMD8D1D8D1ESC00011010ST(1)Сравнить ST(0) c ST(1)FCOM m32fpD810-7 D850-7 D890-7D8/2ESCMF 0MOD=00 MOD=01 MOD=10010R/MСравнить ST(0) c содержимым ячейки памятиFCOM m64fpDC10-7 DC50-7 DC90-7DC/2ESCMF 0MOD=00 MOD=01 MOD=10010R/MСравнить ST(0) c содержимым ячейки памятиFCOM ST(i)D8D0-7D8D0+iESC00011010ST(i)Сравнить ST(0) c ST(i)FCOM2 ST(i)DCD0-7DCD0+iESC10011010ST(i)Сравнить ST(0) c ST(i)FCOMPD8D9D8D9ESC00011011ST(1)Сравнить ST(0) c ST(1) и вытолкнуть ST(0) из стекаFCOMP m32fpD818-F D858-F D898-FD8/3ESCMF 0MOD=00 MOD=01 MOD=10011R/MСравнить ST(0) c содержимым ячейки памяти и вытолкнуть ST(0) из стекаFCOMP m64fpDC18-F DC58-F DC98-FDC/3ESCMF 0MOD=00 MOD=01 MOD=10011R/MСравнить ST(0) c содержимым ячейки памяти и вытолкнуть ST(0) из стекаFCOMP ST(i)D0D8-FD0D8+iESC00011011ST(i)Сравнить ST(0) c ST(i) и вытолкнуть ST(0) из стекаFCOM5 ST(i)DED0-7DED0+iESC11011010ST(i)Сравнить ST(0) c ST(i)FCOMPPDED9DED9ESC11011011ST(1)Сравнить ST(0) c ST(1) и вытолкнуть ST(0) и ST(1) из стекаFTSTD9E4D9E4ESC00111100100Сравнить ST(0) c нулемFICOM m16intDE10-7 DE50-7 DE90-7DE/2ESCMF 0MOD=00 MOD=01 MOD=10010R/MСравнить ST(0) c m16intFICOM m32intDA10-7 DA50-7 DA90-7DA/2ESCMF 0MOD=00 MOD=01 MOD=10010R/MСравнить ST(0) c m32intFICOMP m16intDE18-F DE58-F DE98-FDE/3ESCMF 0MOD=00 MOD=01 MOD=10011R/MСравнить ST(0) c m16int, вытолкнуть ST(0)из стекаFICOMP m32intDA18-F DA58-F DA98-FDA/3ESCMF 0MOD=00 MOD=01 MOD=10011R/MСравнить ST(0) c m32int, вытолкнуть ST(0)из стекаКоманда FUCOM (FUCOMP, FUCOMPP) (сравнение вещественных чисел без учета порядка ”Unordered COMPARE Floating point values”)[/CENTER] Синтаксис команды: FUCOM<DEST>, <SRC> Алгоритм работы: сравнения вещественных чисел без учета порядка. Возможные варианты команды: fucom st(0),st(i) fucomp st(0),st(i) fucompp st(0),st(i) Псевдокод команды: CASE (relation of operands) OF __ST(0)>SRC: C3,C2,C0← 000 __ST(0)<SRC: C3,C2,C0← 001 __ST(0)=SRC: C3,C2,C0← 100 ENDCASE IF ST(0) or SRC=QNaN, but not SnaN or unsupported format __THEN C3,C2,C0← 111 ELSE;ST(0) or SRC=SnaN or unsupported format #IA IF FPUControlWord.IM=1 __THEN C3,C2,C0← 111 __ENDIF ENDIF
Команды FCOMI, FCOMIP, FUCOMI, FCOMIP появились в процессорах P6 и позволяют напрямую заносить результаты сравнения в регистр EFLAGS, не используя команды FSTSW AX/SAHF. ОперацияОпкодESCOPAModOPBR/MДействиеFCOMI ST(0),ST(i)DBF0+iESCP 1111110ST(i)P=0 Сравнить ST(0) и ST(i), установить EFLAGSFCOMIP ST(0),ST(i)DFF0+iESCP 1111110ST(i)P=1 Сравнить ST(0) и ST(i), установить EFLAGS, извлечь из стекаFUCOMI ST(0),ST(i)DBE8+iESCP 1111101ST(i)P=0 Сравнить без учета порядков значения в ST(0) и ST(i), установить EFLAGSFUCOMIP ST(0),ST(i)DFE8+iESCP 1111101ST(i)P=1 Сравнить без учета порядков значения в ST(0) и ST(i), установить EFLAGS, извлечь из стека
Результат выполнения FUCOM SRCCFZFPFЗначение000ST(0) > SRC100ST(0) < SRC010ST = SRC111ST(0) и SRC не могут быть сравнены
ОперацияhexОпкодESCOPAModOPBR/MДействиеРезультатFCOM st(i)D8D0-7D8D0+iESC00011010ST(i)Сравнение вещественного значения, находящегося в ST(0), с оператором, находящемся в памяти или ST(i)Флаги FPUFCOM m32fpD810-7 D850-7 D890-7D8/2ESC000MOD=00 MOD=01 MOD=10010R/MФлаги FPUFCOM m64fpDC10-7 DC50-7 DC90-7DC/2ESC100MOD=00 MOD=01 MOD=10010R/MФлаги FPUFCOMP st(i)D8D8-FD8D8+iESC00011011ST(i)Сравнение вещественного значения, находящегося в ST(0), с оператором, находящемся в памяти или ST(i), с выталкиванием вещественного значения с вершины стекаФлаги FPUFCOMP m32fpD818-F D858-F D898-FD8/3ESC000MOD=00 MOD=01 MOD=10011R/MФлаги FPUFCOMP m64fpDC18-F DC58-F DC98-FDC/3ESC100MOD=00 MOD=01 MOD=10011R/MФлаги FPUFCOMPPDED9DED9ESC11011011ST(1)Сравнивает вещественное значение, находящееся в ST(0) и ST(1), с оператором, находящемся в памяти или в ST(i), с выталкиванием вещественного значения из ST(0) и ST(1)Флаги FPUFCOMIDBF0-7DBF0+iESC01111110ST(i)Сравнение вещественного значения, находящегося в ST(0), с оператором ST(i)Флаги CPUFCOMIPDFF0-7DFF0+iESC11111110ST(i)Сравнение вещественного значения, находящегося в ST(0), с оператором ST(i) с выталкиванием вещественного значения с вершины стекаФлаги CPUFUCOMIDBE8-FDBE8+iESC01111101ST(i)Неупорядоченно сравнивает вещественное значение, находящееся в ST(0), с оператором ST(i)Флаги CPUFUCOMIPDFE8-FDFE8+iESC11111101ST(i)Неупорядоченно сравнивает вещественное значение, находящееся в ST(0), с оператором ST(i) с выталкиванием вещественного значения с вершины стекаФлаги CPUКроме команд сравнения также доступны команды анализа FTST и FXAM. Команда FTST (сравнение вещественных чисел без учета порядка ”TEST Floating point values”)Синтаксис команды: FTST Алгоритм работы: команда FTST сравнивает содержимое вершины стека ST(0) с нулём, и устанавливает коды условий. Результат выполнения FTST C0/CFC3/ZFC2/PFЗначение000ST(0) > 0100ST(0) < 0010ST (0)= 0111ST(0) и 0 не могут быть сравненыКоманда FXAM (проверка числа на вершине стека ”Floating point EXAMINE”)Синтаксис команды: FXAM Алгоритм работы: проверка числа на вершине стека. Команда FXAM устанавливает флаги регистра состояния от C0 до C3, показывая какое число находится на вершине стека. C3/ZFC2/PFC1C0/CFЗначение0000+ ненормализованное0001+ NAN (не число)0010- ненормализованное0011- NAN0100+ нормализованное0101+∞0110- нормализованное0111-∞1000+ 01001Пусто1010- 01011Пусто1100+ денормализованное1101пусто1110- денормализованное1111пустоЕсли очень внимательно рассмотреть таблицу результата выполнения команды FXAM, то можно заметить, что C1 отвечает за знак (C1=0 – знак положительный, C1=1 – знак отрицательный). Если число конечное (ноль, ненормализованное число, нормализованное число, денормализованное число) – С0 = 0, если число неконечное (нечисло, бесконечность, пусто) – С0 = 1. C1=0C1=1C3/ZFC2/PFC0/CF+ненормализованное-ненормализованное000+нормализованное-нормализованное010+0-0100+денормализованное-денормализованное110+NAN (нечисло)-NAN (нечисло)001+∞-∞011ПустоПусто101ПустоПусто111Команда FCMOVcc (Условная пересылка данных ”Floating point Condition MOVE”)Синтаксис команды: FCMOVcc <DEST>, <SRC> Семантика команды: условная пересылка данных. Возможные варианты команды: fcmove st(0),st(i) fcmovne st(0),st(i) fcmovb st(0),st(i) fcmovbe st(0),st(i) fcmovnb st(0),st(i) fcmovnbe st(0),st(i) fcmovu st(0),st(i) fcmovnu st(0),st(i) Псевдокод команды: IF condition TRUE THEN ST(0)← ST(i) ENDIF Это набор команд, каждая из которых копирует содержимое регистра ST(i)) в ST(0), если выполняется соответствующее условие. Реально, каждое условие соответствует тем или иным значениям флагов регистра EFLAGS, но после команд fcom ; (или другие команды сравнения) Код (ASM): fstsw ax sahf fcmovcc в регистр FLAGS загружаются флаги С0, С1 и С3, и последующая команда из набора FCMOVcc приобретает смысл обработки результата предыдущего сравнения. ОперацияОпкодЗначения флаговESCOPAModOPBR/MДействие после FCOMFCMOVB ST(0),ST(i)DAC0-7ZF = 1ESC01011000ST(i)если равноFCMOVE ST(0),ST(i)DAC8-FZF = 0ESC01011001ST(i)если не равноFCMOVBE ST(0),ST(i)DAD0-7CF = 1ESC01011010ST(i)если меньшеFCMOVU ST(0),ST(i)DAD8-FCF = 1 иZF = 1ESC01011011ST(i)если меньше или равноFCMOVNB ST(0),ST(i)DBC0-7CF = 0ESC01111000ST(i)если не меньшеFCMOVNE ST(0),ST(i)DBC8-FCF = 0 иZF = 0ESC01111001ST(i)если не меньше или равноFCMOVNBE ST(0),ST(i)DBD0-7PF = 1ESC01011010ST(i)если несравнимыFCMOVNU ST(0),ST(i)DBD8-FPF = 0ESC01011011ST(i)если сравнимыОперацияhexОпкодESCOPAModOPBR/MДействиеFTSTD9E4D9E4ESC00111100100Сравнить ST(0) с 0.0FXAMD9E5D9E5ESC00111100101Классифицировать значение в ST(0)FNSTSW AXDFE0DFE0ESC11111100000FNSTSW m2byteDD38-F DD78-F DDA8-FDD/7ESC101MOD=00 MOD=01 MOD=10111R/MFNSTSW AXDFE0DFE0ESC11111100000Трансцендентальные функцииЧисла, не удовлетворяющие никакому алгебраическому уравнению с целыми коэффициентами, называются трансцендентальными. π=3,141592... и e=2,71828... – трансцендентальные числа. Если a и b – положительные алгебраические числа, то число loga(b) либо рациональное, либо трансцендентальное. Числа log2(3), lg(5), ln(27) и тому подобные – трансцендентальны. Десятичный логарифм любого целого числа, не изображаемого единицей с нулями (1, 10, 100, 0.1, 0.01) – трансцендентальное число. Трансцендентальные функции – это аналитические функции, не являющиеся алгебраическими. К элементарным трансцендентным функциям относятся: тригонометрические (sin, cos, tg,...); обратные тригонометрические (arcsin, arccos, arctg,...); логарифмические функции (log2x, lg(x), ln(x)); показательные функции (xy, 2y, 10x, ex); гиперболические функции (sh, ch, th, ...); обратные гиперболические (arcsh, arch, arcth, ...); FPU не реализует их все. В нём представлены только основные функции, которые необходимы для вычисления всех остальных возможных. Тригонометрические функции Команда FCOS (вычисление косинуса ”Floating point COSINE)Синтаксис команды: FCOS Семантика команды: команда вычисления косинуса. Псевдокод команды: IF |ST(0)|< 263 _THEN C2← 0 _ST(0)← Cos(ST(0)) ELSE;операнд выходит за пределы _C2← 1 ENDIF Косинус числа, находящегося в ST(0). Операнд считается заданным в радианах и имеет пределы 263 и -263. Если операнд выходит за эти пределы, флаг C2 = 1, а ST(0) не изменяется. Команда FSIN (вычисление синуса =”Floating point SINE)Синтаксис команды: FSIN Семантика команды: команда вычисления синуса. Псевдокод команды: IF |ST(0)|<263 THEN C2←0 ST(0)← Sin(ST(0)) ELSE;операнд выходит за пределы C2← 1 ENDIF Синус числа, находящегося в ST(0). Операнд считается заданным в радианах и имеет пределы 263 и -263. Если операнд выходит за эти пределы, флаг C2 = 1, а содержимое ST(0) не изменяется. Имея вычисленный sin2(x), легко получить cos2(x): sin2(x)+cos2(x)=1 sin(2x) = 2 sin(x) cos(x) [math]cos^{2}(\frac{x}{2})=\frac{cos(x)+1}{2}[/math] [math]cos(2x) = cos^{2}(x)-sin^{2}(x)[/math] Команда FSINCOS (вычисление косинуса и синуса числа =”Floating point SINE and COSINE)Синтаксис команды: FSINCOS Семантика команды: команда вычисления косинуса и синуса числа. Псевдокод команды: IF |ST(0)|<263 _THEN C2← 0 _temp← Cos(ST(0)) _ST(0)← Sin(ST(0)) _TOP← TOP-1 _ST(0)← temp ELSE;операнд выходит за пределы _C2← 1 ENDIF ST(0)←угол в радианах, результат вычислений в ST(0)=Cos(угол) и ST(1)=Sin(угол) Команда FPTAN (вычисление частичного тангенса=”Floating point Partial TANGENT”)Синтаксис команды: FPTAN Семантика команды: команда вычисления частичного тангенса числа. Псевдокод команды: IF |ST(0)|<263 _THEN C2← 0 _ST(0)← tg(ST(0)) _TOP← TOP-1 _ST(0)← 1.0 ELSE;операнд выходит за пределы _C2← 1 ENDIF ST(0)← X ST(1)← Y, где Y/X = tg([math]\alpha[/math]) ST(0)← ST(1)/ST(0)Исходное число – угол α, выраженный в радианах, значение которого должно быть в интервале 0<α< π/4, помещается в вершину стека. Уменьшить угол α до правильного значения можно с помощью команды FPREM. Результатом является отношение Y/X, которое равно тангенсу угла α; Y передается в ST(0), а затем в ST(0) помещается X. Вычисляется тангенс. Результат помещается в ST(0), после чего в ST(0) помещается 1, так что: ST(0) = 1, ST(1) = tg(ST(0)). Единица в ST(0) была нужна для дальнейших вычислений значений sin/cos (инструкции fsin/fcos появились позже, до их появления cos(x) и sin(x) приходилось вычислять через fptan по формулам [math]cos(x)=\frac{1}{\sqrt{1+tg^{2}(x)}}[/math] и [math]sin(x)=\frac{tg(x)}{\sqrt{1+tg^{2}(x)}}[/math] ST(0)=1 и ST(1)=tg(ST(0)) упрощают вычисление ctg(x) вычисляем ctg(x) через fptan Код (ASM): fld x ;st(0)=x fptan ;st(0)=1 st(1)=tg(x) fdivr ; st(0)=1/tg(x)=ctg(x) если котангенс не нужен, то избавится от единицы в ST(0) можно разделив на 1 командой fdivp или вытолкнуть 1 из стека командой fstp st(0) вычисляем COS(π/4) через FPTAN Код (ASM): .data buffer db 100 dup(?) fMtStrinG db " %1.15f",0 . . . .code . . . push eax fldpi push 4 fild dword ptr [esp] fdiv; fdivp st(1),st;3,1415926535897932384626433832795/4=0,78539816339744830961566084581988 fptan;st(1)=tg(pi/4)=0,9999999999999999387 st(0)=1,0 fxch ;fxch st(1) fmul st,st fadd st,st(1);st(0)=2.0 fsqrt;st(0)=1,4142135623730950488016887242097 fdiv; fdivp st(1),st st(0)=0,70710678118654752440084436210485 fstp qword ptr [esp] invoke crt_sprintf,ADDR buffer,ADDR fMtStrinG,dword ptr [esp+4],dword ptr [esp+4] add esp,8 invoke MessageBox,0,ADDR buffer,ADDR aTitle,MB_OK Команда FPATAN (вычисление частичного арктангенса ”Floating point Partial ARCTANGENT”)Синтаксис команды: FPATAN Семантика команды: команда вычисления частичного арктангенса от вещественного числа. Псевдокод команды: IF 0\leq |ST(1)|<|ST(0)|<+∞ THEN ST(1)← arctg(ST(1)/ST(0)) PopRegisterStack ENDIF ST(0)← [math]arctg(\frac{Y}{X})=arctg(\frac{ST(1)}{ST(0)})[/math] Эта функция дополняет предыдущую, FPTAN. Команда FPATAN вычисляет угол в соответствии с отношением чисел ST(1) и ST(0). Она извлекает из стека число X, а затем записывает результирующий угол вместо числа Y в качестве новой вершины стека. Исходные значения должны подчиняться неравенству 0<Y<X<+∞. Вычисляет арктангенс числа ST(1)/ST(0). Результат записывается в ST(1), а ST(0) выталкивается. Результат имеет знак ST(1) и меньше числа p по абсолютной величине. Для вычисления arcsin(x) и arccos(x) используется свойство cos2(x)+sin2(x)=1. [math]tg(x)=\frac{sin(x)}{\sqrt{1-sin^{2}(x)}}=\frac{\sqrt{1-cos^{2}(x)}}{cos(x)}[/math] Обратные тригонометрические функции[math]arcsin(x)=arctg(\frac{x}{\sqrt{1-x^{2}}})[/math] Код (ASM): ; вычисляем арксинус числа, находящегося в st(0) (-1 <= х <= +1) ; по формуле arcsin(x) = arctg(x/sqrt(1-x*x)) ; результат возвращается в st(0), в стеке FPU должно быть ; два свободных регистра fld x ; х (начальное состояние стека) fld st(0) ; st(0)=х st(1)=х fmul st, st(1) ; st(0)=x*x st(1)=x fld1 ; st(0)=1 st(1)=x*x st(2)=x fsubr ; st(0)=1-x*x st(1)=x fsqrt ; st(0)=sqrt(1-x*x) st(1)=x fpatan ; arctg(x/sqrt(1-x*x)) [math]arccos(x)=arctg(\sqrt{\frac{1-x}{1+x}})[/math] [math]arcsec(x)=arccos(\frac{1}{x})[/math] Логарифмические функции Команда FYL2X (вычисление Y·log2X = ”Compute Y·log2(X)”)Синтаксис команды: FYL2X Алгоритм работы: вычисление Y·log2(X) Псевдокод команды: ST(1)← ST(1)·log2(ST(0)) PopRegisterStack Эта функция выполняет операцию логарифмирования. Она берет логарифм по основанию 2 от содержимого ST(0) и затем умножает его на содержимое ST(1). Команда FYL2X извлекает из стека число X и замещает результат числом Y. Исходные числа должны удовлетворять следующим соотношениям: 0<X<+∞ и -∞<Y<+∞ Результат размещается в ST(1), а ST(0) выталкивается из стека. При этом ST(0)>0, если ST(0) = 0, тогда результат (ZM = 1) равен бесконечности со знаком, обратным ST(1). Логарифмические функции на основе FYL2XА что делать, если требуется вычислить логарифм по основанию, не равному 2? С помощью формулы [math]log_{Y}X=\frac{log_{2}X}{log_{2}Y}[/math] вычисляется логарифм числа X по основанию Y. Команды загрузки FLDLG2 (загрузка lg(2)) и FLDLN2 (загрузка ln(2)) позволяют работать с десятичными (бригговыми) и натуральными (неперовыми или гиперболическими) логарифмами: log2(X) = FYL2X(x); ln(X) = ln(2)·log2(X) = FYL2X(ln(2), X) = FYL2X (FLDLN2,X) lg(X) = lg(2)·log2(X) = FYL2X(lg(2), X) = FYL2X (FLDLG2,X) Команда FYL2XP1 (вычисление Y·log2(X+1) = ”Compute Y·log2(X+1)”) Синтаксис команды: FYL2XP1 Алгоритм работы: вычисление Y·log2(X+1) Псевдокод команды: ST(1)← ST(1)·log2(ST(0)+1.0) PopRegisterStack Эта функция идентична функции FYL2X, за исключением того, что к X прибавляется 1. Функция FYL2XP1 накладывает более жесткие ограничения на X и предназначена для вычисления логарифмов чисел, значения которых очень близки к 1. Эта функция дает наиболее высокую точность, если [math]0<|X|< 1-\frac{\sqrt{2}}{2}[/math]. Вычисляет ST(1)·log2(ST(0)+1). ST(1) равен результату, после чего ST(0) выталкивается из стека. При этом первоначальный ST(0) принадлежит [math](-a,a)[/math], где [math]a = 1-\frac{1}{\sqrt{2}}[/math]. Наличие этой команды обусловлено тем, что она даёт большую точность, чем FYL2X для суммы ST(0)+1 для чисел, близких к нулю. Команды загрузки константКоманды загружают в стек заранее известные величины. Все команды загрузки констант не имеют операндов и могут работать только с ST(0). ОперацияОпкодESCOPДействиеFLD1D9E8ESC001.1110.1000Команда загрузки единицы (+1.0)FLDL2TD9E9ESC001.1110.1001Команда загрузки log2(10)=3,32192809488736234780…FLDL2ED9EAESC001.1110.1010Команда загрузки log2(e)= 1,44269504088896340725…FLDPID9EBESC001.1110.1011Команда загрузки числа π = 3,141593265358979323…FLDLG2D9ECESC001.1110.1100Команда загрузки lg(2)=0,3010299956639811952202…FLDLN2D9EDESC001.1110.1101Команда загрузки ln(2)=0,6931471805599453094172…FLDZD9EEESC001.1110.1110Команда загрузки нуля (+0.0)Как можно скрыть команды загрузки констант от "исследователя программ" Если вспомнить формулу [math]log_{A}(B)=1/log_{B}(A)[/math], тогда часть команд для загрузки констант можно спрятать. FP-опкодОписаниечислоэквивалентFLDL2EПомещает константу = log2(e) в стек1,4426950408889634073599246810019=1/ln(2)FLDL2TПомещает константу = log2(10) в стек3,3219280948873623478703194294894=1/lg(2)FLDLG2Помещает константу = lg(2) в стек0,30102999566398119521373889472449=1/log2(10)FLDLN2Помещает константу = ln(2) в стек0,69314718055994530941723212145818=1/log2(e)FLDZ заменить на FSUB st(0),st(0) FLD1 заменить на FDIV st(0),st(0) Замена FLDPI Код (ASM): fld1 fadd st,st;st(0)=2.0 fld st fld st fpatan;ST(1)=arctg(ST(1)/ST(0))=arctg(1.0)=pi/4=0.7853981633974, вытолкнуть из стека fscale; ST(0)=ST(0)x2^{ST(1)} 0.7853981633974 * 4=3.141593265358979323… Команды управления FPUКоманды управления ничего не вычисляют, однако они необходимы для управления FPU. Обычно эти команды требуются в подпрограммах и процедурах прерываний, в которых происходит совместная работа блоков IEU и FPU, а также при обработке ошибок блока FPU. Некоторые команды (например, FINIT/FNINIT) имеют альтернативную мнемонику с символом N, предназначенные для использования в тех случаях, когда команда WAIT не нужна. Команда инициализации FINIT/FNINIT (FPU INITIALIZE) сбрасывает FPU в состояние, в которое он попадает при включении питания. Рекомендуется использовать эту команду перед обращением к FPU. В результате FINIT: rc=0 (режим округления к ближайшему числу); флаги особых ситуаций в регистре SWR сброшены; флаги запрета прерываний в регистре CWR по особым ситуациям установлены; TOP=7. Команда FSTSW/FNSTSW (FPU STORE Status Word) записывает в 16-битную ячейку памяти значение регистра состояния FPU. Допускается использование регистра AX. Команда FSTCW/FNSTCW (FPU STORE Control Word) записывает в 16-битную ячейку памяти значение регистра управления FPU. Команда FFREE ST(i) (FPU FREE register) отмечает атрибут регистра ST(i) в регистре TW как незанятый. Команда FCLEX (FPU CLEAR EXCEPTION) сбрасывает признаки особых ситуаций в регистре SWR. Структура области памяти с рабочей средой сопроцессора Команда FLDCW SRC (FPU LOAD Control Word) записывает в регистр RCW содержимое 16-битной ячейки памяти. Подобная команда для записи в регистр RSW не предусмотрена. Пустая команда FNOP (FPU Not OPERATION) используется для синхронизации совместных действий процессора и FPU и в отладочных целях. Может быть заменена некоторыми командами управления (FNENI - enable interrupts, FNDISI- disable interrupts), унаследованными от i8087, i80287 и недействительными в P6. Команды FDECSTP/FINCSTP (FPU DECREMENT/INCREMENT Stack-Top Pointer) уменьшают/увеличивают величину TOP на единицу, «передвигая» по кольцу значения между регистрами ST(i). Команда FSTENV DEST (FPU STORE ENVIRONMENT) считывает в ячейку DEST (m14byte, если use16 либо m28byte, если use32) в системной памяти содержимое регистров TW, RSW, RCW, FIP, FDP. Устанавливает в RCW флаги запрета прерываний по особым ситуациям. Код (ASM): 401000h: sub esp,28h 401003h: ftst ;<-- получим eip этой команды 401005h: fstenv (28-byte) ptr [esp] 401008h: mov eax,dword ptr [esp+0Ch]; eax=401003h 40100Ch: add esp,28h ;-----esp=18FF64h--FPU Environment-- 18FF64h: FFFF027F; FCW=027Fh 18FF68h: FFFF4541; FST=4541h 18FF6Ch: FFFFFFFF 18FF70h: 00401003; EIP=401003h 18FF74h: 00000023 18FF78h: 00000000 18FF7Ch: FFFF0000 18FF80h: 00000000 18FF84h: 00000000 18FF88h: 00000000 Команда FLDENV SRC (FPU LOAD ENVIRONMENT) записывает в FPU образ его регистров из ячейки SRC (m14byte, если use16 либо m28byte, если use32) системной памяти.
ОперацияhexОпкодESCOPAModOPBR/MДействиеFWAIT/WAIT9B9Bwait for x87 FPU readyFNOPD9D0D9D0ESC=1101.1001.1101.0000No operationsFNENIDBE0DBE0ESC=1101.1101.1110.0000Treated as Integer NOPFNDISIDBE1DBE1ESC=1101.1101.1110.0001Treated as Integer NOPFDECSTPD9F6D9F6ESC=1101.1001.1111.011ii=0 Decrement stack pointer TOP←(TOP-1)& 7FINCSTPD9F7D9F7ESC=1101.1001.1111.011ii=1 Increment stack pointer TOP←(TOP+1)& 7FLDCW m2byteD928-F D968-F D9A8-FD9/5ESC=1101.1001MOD=00 MOD=01 MOD=10101R/MLoad control wordFNCLEXDBE2DBE2ESC=1101.1011.1110.0010Clear exceptionsFNINITDBE3DBE3ESC=1101.1011.1110.0011Initialize MCP RCW←37Fh RSW←0 TW←0FFFFh FDP←0 FIP←0 LastInstructionOpcode←0FSETPMDBE4DBE4ESC=1101.1011.1110.0100Enter Protected Mode, сейчас #UDFFREE ST(i)DDC0-7DDC0+iESC=1101.110111000ST(i)TAG(i)←11bFFREEP ST(i)DFC0-7DFC0+iESC=1101.111111000ST(i)FFREE ST(i) FSTP ST(0)FLDENV m14/28byteD920-7 D960-7 D9A0-7D9/4ESC=1101.1001MOD=00 MOD=01 MOD=10100R/MLoad environment RCW←SRC, RSW←(SRC+2), TW←(SRC+4), FIP←(SRC+6), FDP←(SRC+10)FNSAVE m94/108byteDD28-F DD68-F DDA8-FDD/5ESC=1101.1101MOD=00 MOD=01 MOD=10101R/MSave stateFSTENV m14/28byteD930-7 D970-7 D9B0-7D9/6ESC=1101.1001MOD=00 MOD=01 MOD=10110R/MStore environment DEST←RCW,(DEST+2)←RSW,(DEST+4)←TW,(DEST+6)←FIP, (DEST+10)←FDPFRSTOR m94/108byteDD20-7 DD60-7 DDA0-7DD/4ESC=1101.1101MOD=00 MOD=01 MOD=10100R/MRestore stateFXRSTOR m512byte0FAE08-F 0FAE48-F 0FAE88-F0FAE/10000.1111.1010.1110MOD=00 MOD=01 MOD=10001R/MRestore x87 FPU, MMX, XMM, and MXCS register state from m512byteFXSAVE m512byte0FAE00-7 0FAE40-7 0FAE80-70FAE/00000.1111.1010.1110MOD=00 MOD=01 MOD=10000R/MSave the x87 FPU, MMX, XMM, and MXCSR register state to m512byteКоманда FSAVE DEST (FPU SAVE State) считывает в ячейку DEST (m94byte, если use16 либо m108byte, если use32) в системной памяти данные из FPU целиком, включая R0-R7, и выполняет FINIT. За первыми 14/28 байтами окружения следуют копии R0-R7. Команда FRSTOR SRC (FPU RESTORE State) восстанавливает состояние FPU полностью, в соответствии с образом из ячейки SRC (m94byte, если use16 либо m108byte, если use32) в системной памяти Команда FWAIT/WAIT останавливает центральный процессор до завершения текущей операции в FPU. В P6 имеет смысл только при обработке аппаратных прерываний, возникающих в связи с ошибками в блоке FPU. Опкоды основных команд FPUДля команд с операндом в регистре st(i) используется режим регистровой адресации (Mod=11 R/M=st(i)), для команд с операндом в ячейке памяти выбран режим прямой адресации (Mod=00 R/M=101). Для режимов с косвенной адресацией: Mod=00 R/M=000 - [EAX] R/M=001 - [ECX] R/M=010 - [EDX] R/M=011 - [EBX] R/M=100 - за R/M следует байт SIB R/M=110 - [ESI] R/M=111 - [EDI] Mod=01 R/M=000 - [EAX + 8-битное смещение] R/M=001 - [ECX + 8-битное смещение] R/M=010 - [EDX + 8-битное смещение] R/M=011 - [EBX + 8-битное смещение] R/M=100 - за R/M следует SIB, а далее 8-битное смещение R/M=101 - [EBP + 8-битное смещение] R/M=110 - [ESI + 8-битное смещение] R/M=111 - [EDI + 8-битное смещение] Mod=10 R/M=000 - [EAX + 32-битное смещение] R/M=001 - [ECX + 32-битное смещение] R/M=010 - [EDX + 32-битное смещение] R/M=011 - [EBX + 32-битное смещение] R/M=100 - за R/M следует SIB, а далее 32-битное смещение R/M=101 - [EBP + 32-битное смещение] R/M=110 - [ESI + 32-битное смещение] R/M=111 - [EDI + 32-битное смещение] командаm32fpm64fpm80fpst(i)m16intm32intm64intm80bcdFADD/FIADDD805DC05D8C0+iDE05DA05FMUL/FIMULD80DDC0DDCC8+iDE0DDA0DFCOM/FICOMD815DC15D8D0+iDE15DA15FCOMP/FICOMPD81DDC1DD8D8+iDE1DDA1DFSUB/FISUBD825DC25D8E0+iDE25DA25FDIV/FIDIVD835DC35D8F0+iDE35DA35FLD/FILD/FBLDD905DD05DB2DD9C0+iDF051DB05DF2DDF252FST/FISTD915DD15DDD0+iDF15DB15FSTP/FISTP/FBSTPD91DDD1DDB3DDDD8+iDF1DDB1DDF3DDF35FADDPDEC0+iFMULPDEC8+iFSUBPDEE8+iFDIVPDEF8+iFSUBRPDEE0+iFDIVRPDEF0+i15-11109876543210Добавочные поля111011OPA1MOD1OPBR/MSIBсмещение2MF0MODOPR/MSIBсмещение3MF1MODOPR/MSIBсмещение4DPOPA11OPBST(i)―5001111OP―6011111OP―OP опкод команды, может состоять из двух частей OPA и OPB MF = Memory Format 00m32fp01m32int10m64fp11m16intP = Pop 0Не выталкивает аргумент из стека1Выталкивает из стека после выполнения командыD = Место назначения (Destination) 0ST(0)1ST(i) или память1 Под командой FLD mXXint подразумевается команда FILD mXXint 2 Под командой FLD m80bcd подразумевается команда FBLD m80bcd