Анимация
JavaScript
|
Главная Библионтека Примеры 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
JOOOOOOOH z = x*log2(e) round ( z) z - round(z) ( z- round ( z] (round ( z)) 2z = ex return Для N < 2I0-I можно использовать двойную точность:
; +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 |