Ошибка в том, что вы делаете это:
fstp dword[b]
Это перезаписывает значение b
, поэтому при следующем вызове функции константа будет неправильной. В общем выводе программы это проявляется как крайняя правая оценка, являющаяся единственно правильной, потому что компилятор оценивал аргументы как printf
справа налево. (Допускается вычислять аргументы для функции с несколькими аргументами в в любом порядке, который она хочет .)
Вы должны были использовать раздел .rodata
для своих констант; тогда программа будет аварийно завершать работу, а не перезаписывать константу.
Вы можете избежать необходимости сохранять и загружать промежуточное значение, используя fdivr
вместо fdiv
.
hyp:
fld DWORD PTR [b]
fsub DWORD PTR [esp+4]
fdivr DWORD PTR [a]
fadd DWORD PTR [c]
ret
В качестве альтернативы, делайте то, что делал бы программист Forth, и загружайте константу 1 перед всем остальным, поэтому она должна быть в ST (1), когда это необходимо. Это позволяет вам использовать fld1
вместо помещения 1.0 в память.
hyp:
fld1
fld DWORD PTR [b]
fsub DWORD PTR [esp+4]
fdivp
fadd DWORD PTR [c]
ret
Вам не нужно выдавать finit
, потому что ABI гарантирует, что это уже было сделано во время запуска процесса. Вам не нужно настраивать EBP для этой функции, так как она сама не вызывает никаких функций (жаргонным термином для этого является «конечная процедура») и не нуждается в пустом месте в стеке.
Другая альтернатива, если у вас современный процессор, - использовать более новые инструкции SSE2. Это дает вам нормальные регистры вместо стека операндов, а также означает, что все вычисления фактически выполняются в float
вместо 80-битного расширенного, что может быть очень важно - некоторые сложные числовые алгоритмы будут работать со сбоями, если у них больше число с плавающей запятой точность, чем ожидали дизайнеры. Поскольку вы используете 32-битный ELF ABI, однако, возвращаемое значение все еще нужно свернуть в ST (0), и нет никаких прямых инструкций перемещения между регистрами SSE и x87, вам нужно пройти через память. Я не знаю, как писать инструкции SSE2 в синтаксисе Intel, извините.
hyp:
subl $4, %esp
movss b, %xmm1
subss 8(%esp), %xmm1
movss a, %xmm0
divss %xmm1, %xmm0
addss c, %xmm0
movss %xmm0, (%esp)
flds (%esp)
addl $4, %esp
ret
В 64-битном ELF ABI с возвращаемыми значениями с плавающей точкой в XMM0 (и передачей аргументов в регистрах по умолчанию), это будет просто
hyp:
movss b(%rip), %xmm1
subss %xmm0, %xmm1
movss a(%rip), %xmm0
divss %xmm1, %xmm0
addss c(%rip), %xmm0
ret