Сборка x86-64, устанавливающая флаг переноса для вспомогательной инструкции

Я работаю с книгой по языку ассемблера Ричарда Детмера.

В первой главе говорится:

A borrow occurs in the subtraction a - b when b is larger than a as unsigned numbers. Computer hardware can detect a borrow in subtraction by looking at whether a carry occurred in the corresponding addition. If there is no carry in the addition, then there is a borrow in the subtraction. If there is a carry in the addition, then there is no borrow in the subtraction.

Флаг переноса - это 0-й бит регистра EFL.

Предположим, мы хотим выполнить 195D - 618D = -423D в качестве операции вычитания. Имеется заимствование, поэтому флаг переноса устанавливать не следует.

Следующий код asm компилируется и запускается, однако после sub rax, 618 флаг переноса действительно установлен.

Соответствующее добавление было бы 00C3h + FD96h, и это не связано с переносом, поскольку окончательное попарное сложение - это 0 + F без переноса в него, и, следовательно, нет переноса из последнего попарного сложения.

 .DATA
number  QWORD   195
sum     QWORD   ?

.CODE
main    PROC
        mov     rax, number     ; 195 to RAX
        sub     rax, 618        ; subtract 618
             ;at this point, however, the carry flag is indeed set to 1. Why is this?
        mov     sum, rax        ; sum to memory

        mov     rax, 0          ; return code
        ret                     ; exit to operating system

main    ENDP

END

Я не понимаю, как это могло быть.

Любая помощь будет оценена по достоинству.


person Tryer    schedule 16.11.2017    source источник
comment
Это просто недоразумение, заимствование для SUB действительно сигнализируется флагом переноса. Специального флага заимствования нет, флаг переноса может выполнять обе задачи. Нет никакой двусмысленности, так как вы знаете, сделали ли вы только что ADD или SUB.   -  person Hans Passant    schedule 16.11.2017
comment
@HansPassant Да, это моя оплошность. Далее в тексте действительно говорится о влиянии add и sub на флаги, и то, что вы упомянули, действительно разъясняется.   -  person Tryer    schedule 16.11.2017
comment
Интересный факт: в некоторых архитектурах, отличных от x86 (например, ARM), флаг переноса по-прежнему является переносом, а не заимствованием для вычитания. Таким образом, он в основном инвертирован по сравнению с тем, как его использует x86. См. en.wikipedia.org/wiki/Carry_flag#Carry_flag_vs._borrow_flag. Вот почему в ARM есть инструкция sbc (подпрограмма с переносом) вместо sbb x86 (подпрограмма с заимствованием).   -  person Peter Cordes    schedule 16.11.2017


Ответы (1)


Сначала поймите, что существует целочисленная арифметика без знака (где переполнение обозначается флагом переноса) и целочисленная арифметика со знаком (где переполнение обозначается флагом переполнения).

Те же самые инструкции сложения и вычитания используются как для беззнаковой, так и для знаковой целочисленной арифметики. Единственная разница заключается в том, какие флаги вы тестируете потом и как интерпретируете результат (например, как -0x0000423D или как 0xFFFFBDC3).

Заимствование также обозначается флагом переноса. Это означает, что заимствование происходит всякий раз, когда происходит переполнение беззнакового целого числа. Для 0x0000195D - 0x0000618D существует переполнение целого числа без знака (целые числа без знака не могут быть отрицательными), поэтому будет установлен флаг переноса (но не было переполнения целого числа со знаком, поэтому флаг переполнения не будет установлен). Результатом будет 0xFFFFBDC3 или -0x0000423D в зависимости от того, должен ли результат быть подписанным или беззнаковым.

person Brendan    schedule 16.11.2017