Я работал с Professional Assembly Language Ричарда Блюма, и его начальная программа сравнения с плавающей точкой работает , но при запуске через gdb на Linux некоторые промежуточные шаги неожиданны .
Для эксперимента:
Вот программа (были добавлены мои комментарии, и я удалил nop, который не нужен при отладке дварфа):
.section .data
value1:
.float 10.923
value2:
.float 4.5532
.section .text
.globl _start
_start:
flds value1 # Load a 32-bit floating-point value into the FPU stack
fcoms value2 # Compare the value in ST0 with mem address &value2
# Retrieving the value of the FPU status register (with no argument ...
fstsw # ... this defaults to AX)
sahf # Store AH into Flags: 00 => CF, 02 => PF, 06 => ZF
ja greater
jb lessthan
movl $1, %eax # Exit (condition: value1 = value2)
movl $0, %ebx
int $0x80
greater:
movl $1, %eax # Exit (condition: value1 is greater than value2)
movl $2, %ebx
int $0x80
lessthan:
movl $1, %eax # Exit (condition: value1 is less than value2)
movl $1, %ebx
int $0x80
(у меня также есть версия, переведенная и работающая в синтаксисе nasm, если кто-то захочет это сделать - но мой быстрый тест этого, похоже, не дал других результатов.)
Он построен и связан с:
as -o fcomtest.o fcomtest.s --32 --gdwarf-2
ld -o fcomtest fcomtest.o -m elf_i386
Я использую следующий gdb-input-script (возможно, вам придется добавить или удалить команду 'step' в последних двух строках в зависимости от того, какие значения1 и значение2 установлены и какая ветвь берется):
# suppressing the output when setting the breakpoint at _start
set logging file /dev/null
set logging redirect on
set logging on
br _start
set logging off
printf "\nFirst outputting values1 and 2:\n"
x/f &value1
x/f &value2
# suppressing the output when the breakpoint is triggered
set logging on
run
set logging off
printf "Now, ST0 = %f\n", $st0
printf "\nChecking the intial value of the FPU status register...\n"
print/t $fstat
printf "\nfcoms value2 # Comparing the value in ST0 with mem address &value2\n\n"
set logging on
step
set logging off
printf "The FPU status register FSTAT now contains...\n"
print/t $fstat
printf "\nBefore copying FSTAT, the AX register contains: "
print/t $ax
printf "\nfstsw # Copying (16-bit) FPU status register, FSTAT, to AX"
set logging on
step
set logging off
printf "\n\nNow the contents of the AX register are: \n"
print/t $ax
printf "\nInitially, the EFLAGS register in binary is: \n"
print/t $eflags
printf "\nThat is, the flags set are: "
print $eflags
printf "\nsahf # store the high 8-bits of AX into the corresponding flags "
set logging on
step
set logging off
printf "\n\nNow, the EFLAGS register contains: \n"
print/t $eflags
printf "\nIn other words, these flags are set: "
print $eflags
printf "\nNow branch according to the CF and ZF flags...\n"
step
step
step
step
q
Наконец, чтобы проверить выходные данные отладчика и воспроизвести это легко, я использую эту командную строку:
$ gdb -q -x gdb-input-script > gdb.output fcomtest
Вы можете затем см. вывод, взбитый с:
$ cat gdb.output
.
Теория Существует дискуссия о механизме , как это должно работать в следующих постах: x86 ассемблер: сравнение с плавающей запятой , Сборка: JA и JB работают некорректно , а в связанных статьях.
В частности, предполагается, что FCOM сравните значение в ST0 стека FPU и другое значение и измените биты кода C3, C2 и C0 регистра состояния FPU в соответствии с:
+-----------------+----+-----+----+
| Condition | C3 | C2 | C0 |
+-----------------+----+-----+----+
| ST0 > argument | 0 | 0 | 0 |
| ST0 < argument | 0 | 0 | 1 |
| ST0 = argument | 1 | 0 | 0 |
+-----------------+----+-----+----+
Кроме того, SAHF должен отобразить спецификацию c биты для EFLAG. Вместо этого: вот краткое описание того, что на самом деле происходит с вводом этих значений («руководство» - это документация Intel):
** Странная часть **
Case 4II (value1 +ve, value2 +ve; val1 greater magnitude, val2 lesser magnitude)
15 14 13 12 11 10 09 08 -- FPU status reg.
FPU C3 SP SP SP C2 C1 C0
07 06 05 04 03 02 01 00 -- AH register
1 1 1 0 0 0 0 0
[In this result of FCOM: C3 has been set to 1 -- not what was expected. The
expected result was C3 = 0, C2 = 0, C0 = 0]
SAHF instruction:
From the manual: 07 => SF, 06 => ZF, 04 => AF, 02 => PF, 00 => CF
From the textbook: 06 => ZF, 02 => PF, 00 => CF
Actual behavior: -07 => SF, -06 => ZF , -04 => AF, 02 => PF ?, 00 => CF ? ]
EFLAGS register:
0 0 0 0 0 0 1 0 -- Before SAHF
SF ZF - AF - PF - CF
0 0 0 1 0 0 1 0 -- After SAHF
[Here, CF = 0 and ZF = 0, so the JA branch is taken and the result is as
desired]
Я знаю, что это было несколько долго (но это показывает, как восстановить это очень легко). В итоге: если вы измените значения 1 и 2 и перекомпилируете, я обнаружу, что на самом деле происходит следующее - по крайней мере, в выводе gnu-debugger:
C3 C2 C0
1 0 0 -- Equal numbers case (behavior as described in the documentation)
-----------------------------------------------
1 1 0 -- Value1 < Value2, actual
(SAHF acts as though it has a NOT applied before altering the flags)
(The status register itself seems to be the inverse of the expected:)
0 0 1 -- expected
-----------------------------------------------
1 0 0 -- Value1 > Value2
0 0 0 -- expected
(Output of SAHF contorts somehow to permit CF = ZF = 0 )
I я описал, как воспроизвести это, так что это просто копирование для просмотра результатов. Является ли это просто странностью в gdb, которая изменяет значения C3, C2 и C0, а затем настраивает их так, чтобы флаги работали? Во всех случаях правильная ветвь в конечном итоге берется ... Я не экспериментировал (не практиковался) с другими отладчиками, чтобы увидеть, не отладчик ли просто выполняет промежуточные шаги для случаев value1! = Value2.