Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 [ 74 ] 75 76 77 78 79 80 81 82 83 84 85 86 87 88

Примеры

FISTP [тет32] WAIT

MOV ЕАХ, [mem32]

FILD [mem32] WAIT

MOV [mem32],EAX

FLD QWORD PTR WAIT

ADD ESP,8

; ждем, пока FPU запишет в память, а потом.. считываем результат модулем целочисленных вычислений

; ждем, пока FPU считает значение из памяти.. ; перед ее перезаписью целым числом

[ESp;

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

Инструкции WAIT для координации доступа к памяти были действительно нужны для 8087 и 80287, но на Pentium она в этом качестве совершенно не обязательна. Что касается 80386 и 80486, руководства от Intel говорят, что WAIT необходима для этой цели, не считая инструкций FNSTSW и FNSTCW хотя и их пропуск не приводит к ошибкам. Пропуск инструкций WAIT для координирования доступа к памяти не очень надежен даже при написании 32-битного кода, потому что код может быть выполнен на очень редкой комбинации 80386 процессора с 287 сопроцессором, который требует WAIT.

Чтобы быть уверенным в том, что код будет работать на любом 32-битном процессоре (включая неинтеловские процессоры), рекомендуется использовать WAIT в этом качестве на всякий случай.

WAIT иногда используется, чтобы следить за исключениями. Оно может генерировать прерывание, если бит исключения в слове статуса FPU был установлен предыдущей операцией плавающей запятой.

Ассемблер автоматически вставляет WAIT для этих целей перед следующими инструкциями: FCLEX, FINIT, FSAVE, FSTCW, FSTENV, FSTSW. Можно пропустить WAIT перед FNCLEX и т. п. Тесты показывают, что в большинстве случаев WAIT не нужен, потому что эти инструкции без WAIT все равно будут генерировать прерывания или исключения, кроме FNCLEX и FNINIT на 80387. (Есть некоторая неопределенность, касаемая того, указывает ли IRET от прерьшания на инструкцию FN или на следующую инструкцию).

Почти все инструкции с плавающей запятой будут также генерировать прерывание, если предыдущая инструкция с плавающей запятой установила бит исключений, поэтому исключение рано или поздно будет обнаружено. Можно вставлять WAIT после последней инструкции с плавающей запятой, чтобы обработать все возникшие исключения.

6.26.7. FCOM + FSTSW АХ (все процессоры)

Инструкция FNSTSW очень медленна на любых процессорах. У процессоров РРго, и РЗ есть инструкции FCOMI, как ее замена. Использование FCOMI вместо обычной последовательности FCOM / FNSTSW АХ / SAHF экономит 8 тактов. Поэтому следуе

„спользовать FCOMI, чтобы избегать применения FNSTSW везде, где это возможно, даже если это будет стоить дополнительного кода.

На процессорах, у которых отсутствует инструкция FCOMI, обычной практикой сравнения значений с плавающей запятой является последовательность:

FLD [а] FCOMP [Ь] FSTSW АХ SAHF

JB ASmallerThanB

Можно улучшить этот код, использовав FNSTSW АХ вместо FSTSW АХ и протестировав АН напрямую, а не используя неспариваемый SAHF (у TASM 3.0 есть баг, связанный с инструкцией FNSTSW АХ):

FLD [а] FCOMP [Ь] FNSTSW АХ SHR АН,1

JC ASmallerThanB Тестирование на ноль или равенство:

FTST

FNSTSW АХ AND АН,40Н

JNZ IsZero ; (флаг нуля инвертирован!)

Проверка, больше ли одно значение другого:

FLD [а]

FCOMP [Ь]

FNSTSW АХ

AND АН, 41Н

JZ AGreaterThanB

Не рекомендуется использование TEST АН,41Н, так как она не спаривается на Р1 и РММХ. На Р1 и РММХ инструкция FNSTSW занимает 2 такта, но она вызывает задержку в дополнительные 4 такта после любой инструкции с плавающей запятой, потому что она ожидает слово статуса FPU. Этого не происходит после целочисленных инструкций. Можно заполнить промежуток между FCOM и FNSTSW целочисленными инструкциями на 4 такта. Спареваемая FXCH сразу после FCOM не задерживает FNSTSW, даже ЕСЛИ спаривание несовершенное.

FCOM ; такт 1

FXCH ; такты 1-2 (несовершенное спаривание)

INC DWORD PTR [ЕВХ] ; такты 3-5 FNSTSW АХ ; такты 6-7



Здесь можно использовать FCOM вместо FTST, потому что FTST не спаривается Не следует забывать включить N в FNSTSW. У FSTSW (без N) префикс WAIT, которые задержит ее в дальнейшем.

Иногда быстрее использовать целочисленные инструкции для сравнения значений с плавающей запятой, как это объяснено в п. 6.27.6.

6.26.8. FPREM (все процессоры)

Инструкции FPREM и FPREMI медленны на всех процессорах. Их можно заменить следующим алгоритмом; умножить на обратное делителю число, получить дробную часть, усечь целую, затем умножить на делитель (см. п. 6.27.5, чтобы узнать, как усекать значения).

В некоторых документах говорится о том, что эти инструкции могут давать неполную редукцию, и поэтому необходимо повторять инструкции FPREM и FPREM 1, пока она не будет получена.

6.26.9. РЬШВШТ (все процессоры)

Эта инструкция медленна на всех процессорах. Ее можно заменить на:

FISTP QWORD PTR [TEMP] FILD QWORD PTR [TEMP]

Этот код быстрее, несмотря на возможные потери из-за попытки считать [TEMP], когда запись еще не окончена. Здесь рекомендуется поместить какие-нибудь доплни-тельные инструкции.

6.26.10. FSCALE и экпоненциальная функция

(все процессоры)

FSCALE медленна на всех процессорах. Вычислить целочисленные степени числа 2 можно гораздо быстрее, вставив желаемую степень в поле экспоненты числа с плавающей запятой. Вычислить 2N, где N - целое число со знаком, можно сделать одним из следующих способов.

Для N < 27-1 можно использовать одинарную точность:

MOV ЕАХ, [N]

SHL ЕАХ, 23

ADD ЕАХ, 3F800000H

MOV DWORD PTR [TEMP], ЕАХ

FLD DWORD PTR [TEMP]

MOV ЕАХ, [N]

SHL ЕАХ, 20

ADD ЕАХ, 3FF00000H

MOV DWORD PTR [TEMP],

MOV DWORD PTR [TEMP+4:

FLD QWORD PTR [TEMP]

Для N < 214-1 используя длинную двойную точность:

MOV ЕАХ, [N]

ADD EAX, 00003FFFH

MOV DWORD PTR [TEMP] , 0

MOV DWORD PTR [TEMP+4], 80000000H

MOV DWORD PTR [TEMP+8], EAX

FLD TBYTE PTR [TEMP]

FSCALE часто используется в вычислениях экспоненциальных функций. Следующ» код показывает экспоненциальную функцию без медленных FRNDINT и FSCALE:

; extern "С" ехр PROC PUBLIC ехр

long double cdecl exp (double x) NEAR

FLDL2E

QWORD PTR [ESP+4]

FMUL

FIST

DWORD PTR [ESP+4]

ESP, 12

DWORD PTR [ESP] , 0

DWORD PTR [ESP+4],

FISUB

DWORD PTR [ESP+16]

EAX, [ESP+16]

EAX,3FFFH

[ESP+8],EAX

SHORT UNDERFLOW

EAX,8000H

SHORT OVERFLOW

F2XM1

FLDl

FADD

TBYTE PTR [ESP]

ESP,12

FMUL

UNDERFLOW:

FSTP

FLDZ

JOOOOOOOH

z = x*log2(e) round ( z)

z - round(z)

( z- round ( z] (round ( z))

2z = ex

return

Для N < 2I0-I можно использовать двойную точность:



ESP,12

OVERFLOW:

PUSH

07F800000H

FSTP

DWORD PTR [ESP]

ESP,16

; +infinity

; return infinity

exp

ENDP

6.26.11. FPTAN (все процессоры)

Согласно руководствам, FPTAN возвращает два значения X и Y и оставляет программисту деление Y на X для получения окончательного результата, но фактически она всегда возвращает в X 1 поэтому можно сэкономить на делении. Тесты показывают, что на всех 32-битных процессорах Intel с модулем плавающей запятой или сопроцессором, FPTAN всегда возвращает 1 в X независимо от аргумента. Если есть желание быть абсолютно увереными, что код будет выполняться корректно на всех процессорах, можно протестировать, равен ли X одному, что быстрее, чем деление на X. Значение У может быть очень велико, но не бесконечно, поэтому не надо тестировать, содержит ли У правильное число, если известно, что аргумент верен.

6.26.12. FSQRT (РЗ)

Быстрый способ вычислить приблизительное значение квадратного корня наРЗ -умножить обратный корень от х на сам х:

SQRT(x) = X * RSQRT(x) Инструкция RSQRTSS или RSQRTPS дает обратный корень с точностью 12 бит. Можно улучшить точность до 23 бит, используя формулу Ньютона-Рафсона, использованную в интеловской сопроводительной заметке АР-803:

хО = RSQRTSS(а)

х1 = 0.5 * хО * (3 - (а * хО)) * хО), где хО - это первое приближение к обратному корню от а, а х1 - лучшее приближение. Порядок вычисления имеет значение. Можно использовать эту формулу до умножения, чтобы получить квадратный корень.

TEST ЕСХ,ЕСХ TEST [mem],ЕВХ TEST EDX,256 TEST DWORD PTR

[EBX],8000H

спаривается спаривается не спаривается не спаривается

Чтобы сделать их спариваемыми, нужно использоватьт один из следующих методов:

MOV ЕАХ,[ЕВХ] / TEST.ЕАХ,8000Н MOV EDX, [ЕВХ] / AND EDX, 8000Н MOV AL,[EBX+1] / TEST AL,80H MOV AL,[EBX+1] / TEST AL,AL ;

(результат в флаге знака)

6.26.13. MOV [MEM], ACCUM (PI и РММХ)

Инструкции MOV [mem],AL, MOV [mem],AX, MOV [mem],EAX расцениваются механизмом спаривания как пишущие в аккумулятор. Поэтому следующие инструкции не спариваются:

MOV [mydata], ЕАХ MOV ЕВХ, ЕАХ

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

MOV самостоятельно.

В 32-битном режиме можно записать основную форму MOV [mem],EAX следующим

образом:

DB 89Н, 05Н DD OFFSET DS:mera В 16-битном режиме можно записать основную форму MOV [mem],AX так:

DB 89Н, 06Н

DW OFFSET DS:raera Чтобы использовать AL вместо (Е)АХ, нужно заменить 89Н на 88Н. Этот изъян не был исправлен в РММХ.

6.26.14. Инструкция TEST (PI и РММХ)

Инструкция TEST с числовым операндом спаривается только, если назначением являются AL, АХ или ЕАХ.

TEST регпстр,регистр и TEST регистр,память всегда спаривается.



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 [ 74 ] 75 76 77 78 79 80 81 82 83 84 85 86 87 88