Взял кусок из функции FpuComp, для сравнения двух 4-байтных float Код (Text): singleFunc proc single1:DWORD,single2:DWORD LOCAL prevstate[108] :BYTE fxam fstsw ax fwait sahf fsave prevstate lea eax,single1 fild dword ptr [eax] lea eax,single2 fild dword ptr [eax] fxch fcom fstsw ax fwait shr al,1 sahf ja @G jc @E PrintText "=" ;single1 = single2 jmp @EndCmp @E: PrintText "<" ;single1 < single2 jmp @EndCmp @G: PrintText ]" ;single1 > single2 @EndCmp: frstor prevstate сравниваю -1.5 и -1.1 получается -1.5 > -1.1. Это я ошибся в коде, или действительно -1.5 > -1.1 ? Пробовал непосредственно mov eax,single1 mov ecx,single2 invoke FpuComp,eax,ecx,SRC1_DIMM or SRC2_DIMM результат тот же И ещё одна пара чисел: -1.5 Е01 < -1.1 E02 (-15 < -110)???
cresta Почитай внимательно описание команд FCOMxx. Сравнение происходит между регистром st(0) и источником. Если источник не указан, то подразумевается, что сравнивается st(0) и st(1). Итого либо надо операнды в сопроцессор помещать, начиная со второго, либо интерпретировать результаты сравнения наоборот. Если не затуманивать код сохранением состояния сопроцессора, то он должен выглядеть так: Код (Text): align 4 fcompare proc f1 : dword, f2 : dword option prologue : none option epilogue : none fld dword ptr [esp+4] ;; f1 fld dword ptr [esp+8] ;; f2 fcompp fnstsw ax sahf ja f2_great_f1 je f2_equal_f1 f1_great_f2: mov eax,1 ret 8 f2_equal_f1: xor eax,eax ret 8 f2_great_f1: or eax,-1 ret 8 option prologue : prologuedef option epilogue : epiloguedef fcompare endp align 4 PS Почему для загрузки чисел с плавающей точкой ты используешь команду fild?
q_q В справочнике по опкодам ни слова нет про команды, начинающиеся с f... Всё что есть мало-мальски комментированное - это исходник FpuComp, из него и пытаюсь понять, пошел по ветке размера 4 байт и проглядел, что целое число, а по ветке 10 байт грузится fld tbyte ptr [eax]. - с запятой Если загружать fld qword ptr [eax], то правильно ли загрузится число размером в 8 байт (double)? Если да, то логично было бы добавить в FpuComp возможность работы с qword'ами наравне с 4 и 10 байтными числами. option prologue : none option epilogue : none Эти инструкции предотвращают вставку конструкций типа push ebp mov ebp,esp add esp,xxx это так?
cresta В справочнике по опкодам ни слова нет про команды, начинающиеся с f... Не те справочники используешь. Например, masm32\help\fpuhelp.hlp или Зубков - "2.4.6 Команды сравнения FPU" в имеющемся у меня издании 90-ая страница. Imho давно пора качнуть "IA-32 Intel (R) Architecture Software Developer's Manual" в трех томах. Конкретно во втором томе "3.2 Instruction reference" страница 252 "FCOM/FCOMP/FCOMPP - Compare Floating Point Values". В аттаче скриншот. Всё что есть мало-мальски комментированное - это исходник FpuComp Почитай к нему справку. Код (Text): SRC1_FPU Src1 is already on the FPU SRC1_REAL Src1 is a pointer to an 80-bit REAL number SRC1_DMEM Src1 is a pointer to a 32-bit signed integer SRC1_DIMM Src1 is a 32-bit signed integer SRC1_CONST Src1 is one of the special FPU constants SRC2_FPU Src2 is already on the FPU SRC2_REAL Src2 is a pointer to an 80-bit REAL number SRC2_DMEM Src2 is a pointer to a 32-bit signed integer SRC2_DIMM Src2 is a 32-bit signed integer SRC2_CONST Src2 is one of the special FPU constants Где тут сравнение 4-ехбайтных с плавающей точкой. Если загружать fld qword ptr [eax], то правильно ли загрузится число размером в 8 байт (double)? Да. Сформируй из Код (Text): int dc(double d1, double d2) { if (d1 > d2) return 1; if (d1 < d2) return -1; return 0; } int fc(float f1, float f2) { if (f1 > f2) return 1; if (f1 < f2) return -1; return 0; } asm-код и посмотри какие команды поставит компилятор. Эти инструкции предотвращают вставку конструкций типа ... это так? Да. Еще сам обязан позаботиться о выталкивании из стека параметров, если этого требует соглашение о вызове подпрограммы. 985806725__FCOMxx.GIF
Ну говорю ж, проглядел, что integer, а не float Для восьбайтных уже нащупал, правда в стеке запутался, поэтому без option prologue : none option epilogue : none : Код (Text): doubleCmpFunc proc double1:QWORD,double2:QWORD lea eax,double1 lea ecx,double2 push ecx push eax call fdblcompare PrintDec eax ret doubleCmpFunc endp fdblcompare proc f1Addr : dword, f2Addr : dword mov eax,f1Addr fld qword ptr [eax] mov eax,f2Addr fld qword ptr [eax] fcompp ..... А что сделает C-compiler, сейчас посмотрю. Спасибо.
Значит размер загружаемого и сравниваемого определяется D94424 или DD4424. И второй операнд можно не загружая сравнивать. fcomp dword/qword ptr[]
cresta размер загружаемого и сравниваемого определяется D94424 или DD4424 Не совсем так. Размер загружаемого - да. Размер сравниваемого - нет. Afaik в сопроцессоре числа лежат в одном формате - double extended-precision floating-point format. второй операнд можно не загружая сравнивать Да.
cresta, q_q Не забывайте очищать стэк FPU при выходе из функции. Использование fsave\frstor это большие тормоза. Поэтому нужно просто аккуратно следить за состоянием стека FPU: если мы загрузили два регистра fld\fild, то должны их освободить. В данном случае - это fcompp + нужно добавить fstp st(0). Иначе после нескольких вызовов функции можем получить исключение #IS - переполнение стека FPU.
leo В данном случае - это fcompp + нужно добавить fstp st(0). Зачем? Судя по документации PP обеспечивает pop register stack twice.
q_q Точно, прошу прощения. Я кстати о такой фиче или не знал или забыл, так что еще спасибо за информацию.
Народ а "fcomi" и его аналоги что не в моде, вроде только P6 надо? IMHO плюс от "fcom", что при проверке диапазона можно обойтись 1 джампом.
Но при этом есть минус: для fcomi оба значения должны быть загружены в регистры. fcom же позволяет отказаться от одной загрузки: D8 /2 FCOM m32fp Compare ST(0) with m32fp. DC /2 FCOM m64fp Compare ST(0) with m64fp. отказываясь от перехода, до выполнения которого дело может и не дойти, получаем ещё один fld. Кажется для случая единичного сравнения двух чисел это не есть гут. А если условие <= >= и т.п. то переход всего один. Или не так? fcomi/fcomip хорош, когда надо сравнить несколько float'ов между собой. Тогда экономятся загрузки.
1 джамп (условный), поясню что имел: Код (Text): fld [TestValue] fcom [LowBound] fnstsw ax xchg eax,edx fcomp [HiBound] fnstsw ax xor ah,dh jnz .InRange вроде так.
cresta > "Но при этом есть минус..." Минус очень относительный, т.к. экономия одного fld м.б.важна только в случае нехватки регистров. Если ограничения нет, то fld[m]+fcomi+fstp получается всего на 1 байт больше (а без fstp покороче), но зато на несколько тиков быстрее (особенно на P6), чем fcomp[m]+fnstsw+sahf. А потому, что fnstsw - довольно тормозная штука. Цитата из pentopt.pdf by Agner Fog (раздел 18.10): "The FNSTSW instruction is very slow on all processors. Using FCOMI instead of the common sequence FCOM / FNSTSW AX / SAHF will save 8 clock cycles on PPro, P2 and P3, and 4 clock cycles on P4. You should therefore use FCOMI to avoid FNSTSW wherever possible, even in cases where it costs some extra code."
> "А где можно для команд f.... найти данные о тиках?" 1) Официальные, но неполные данные для P4 см. в IA-32 Intel® Architecture Optimization Reference Manual (см. мануалы от Intel) 2) Полные, но неофициальные данные по всем пентиумам см. Agner Fog "How to optimize for the Pentium® microprocessors" (pentopt.pdf)
IA-32 (2 и 3 том) есть, но что-то я или проглядел или инфа в первом томе, а по ссылке на Фога сейчас схожу. спасибо
cresta IA-32 Optimization - это отдельный мануал (24896611.pdf, 2.55 MB). На указанной страничке Intel он идет после 3-го тома.