Рассматривает ли архитектура x86 двоичное вычитание нуля / сравнение с нулем особым образом? - PullRequest
0 голосов
/ 04 ноября 2019

Вновь обращаясь к сборке x86, я столкнулся с ситуацией, в которой я пытаюсь понять, как можно объяснить поведение процессора.

При сравнении двух целых чисел без знака с помощью команды cmp состояниеперенос и нулевой флаг отражают отношение обоих целых чисел. Поскольку cmp ведет себя как команда sub без записи результата вычитания в окончательный регистр, флаг переноса принимает участие в флаге заимствования. Кроме того, вычитание является не чем иным, как сложением инвертированного значения. Таким образом, вместо вычитания 1, процессор добавляет -1.

Теперь, когда вычитание меньше вычитаемого, добавление во время cmp не вызовет переноса, но из-за необходимого заимствования должен быть установлен флаг переноса. ,Таким образом, вы можете сказать, что флаг переноса должен быть инвертирован после сложения, чтобы получить правильное состояние.
Предыдущее предположение работает для каждой комбинации целых чисел без знака с одним исключением:

Вычитание нуля из любого целого числа без знакаэто то же самое, что добавить к ним ноль, и это не приводит к переносу. Таким образом, инвертированный перенос будет приводить к установленному флагу переноса, который является неправильным, потому что minuend (<0) больше, чем вычитаемое значение (0), и поэтому флаг переноса не может быть установлен, потому что заимствование не требовалось. </p>

Я проверил это поведение с помощью небольшой ассемблерной программы и, конечно, процессор не выдает установленный флаг переноса. Но это возможно только в том случае, если процессор также проверяет, что вычитаемое значение равно нулю (а minuend не равно нулю, minuend = subtrahend -> установлен ноль флагов).

Прав ли я со своими предположениями или нетчто-то, чего мне не хватает?

1 Ответ

4 голосов
/ 04 ноября 2019

Ваша интуиция почти верна. Вот пропущенный бит: вычитание может быть реализовано путем сложения путем дополнения как миниуенда, входящего переноса (0 для sub, становящегося 1) и исходящего переноса. Когда вы вычитаете 0 из некоторого числа, то добавляется 0xffffffff (при условии 32-битного ЦП) и 1 из инвертированного переноса. Таким образом, мы фактически добавляем 0x100000000, устанавливая исходящий перенос (т.е. очищая исходящий заем). В этом разница с вашей моделью, в которой будет добавлено 0, а не установка исходящего переноса (т. Е. Установка исходящего заимствования),

...