movzx и cwd - они взаимозаменяемы? - PullRequest
0 голосов
/ 31 января 2020

У меня есть эти два фрагмента кода:

mov ax, word [wNum2]
cwd
div word [wNum3]
mov [wAns16], dx
movzx eax, word [wNum2]
;cwd
div word [wNum3]
mov [wAns16], edx

Первый даст правильный ответ, второй даст мне ответ, который будет сто или около того, если я не раскомментирую cwd.

Мой вопрос в том, что я думал, что movzx обнулит все для меня, и это сделает cwd ненужным. Я совершенно не понял, как они работают? Может ли кто-нибудь провести меня через эти фрагменты кода?

Ответы [ 2 ]

6 голосов
/ 31 января 2020

Чистый результат может быть эквивалентным или нет - это зависит от значения. Описание состояний CWD

Удваивает размер операнда в регистре AX, EAX или RAX (в зависимости от размера операнда) посредством расширения знака и сохраняет результат в регистрах DX: AX, EDX: EAX или RDX: RAX соответственно. Инструкция CWD копирует знак (бит 15) значения в регистре AX в каждую позицию бита в регистре DX.

Таким образом, если значение в AX меньше 32 767 (15-битовое МАКС.), Результат будет эквивалентен MOVZX (расширение нуля) и MOVSX (расширение знака). Но если значение больше, только будет эквивалентно MOVSX. Обычно MOVZX используется в сочетании с DIV (беззнаковое деление) и MOVSX в сочетании с IDIV (знаковое деление).

Но остается проблема, где будет храниться результат :
CWD сохраняет 32-битный результат в двух 16-битных регистрах DX:AX, а инструкции MOV?X сохраняют его в 32-битном регистре EAX.

Это имеет последствия по следующей DIV инструкции. Первая часть вашего кода использует 32-битное значение в DX:AX в качестве ввода, в то время как второй подход предполагает EAX в качестве ввода 16-битного DIV:

F7 /6   DIV r/m16   M   Valid   Valid   Unsigned divide DX:AX by r/m16, with result stored in AX ← Quotient, DX ← Remainder. 

, который делает результат непредсказуемым, поскольку DX не определено, а верхняя половина EAX не используется в делении.

1 голос
/ 01 февраля 2020

Нет, MOVZX - нулевое расширение, а не знак. И CWD подписывает AX в DX: AX (как вы хотели до I DIV, а не DIV).

movSx eax, word [wNum2] - более эффективный способ сделать mov ax,mem + CWD E , не CWD. (Если известно, что ваши входные данные являются неотрицательными при обработке со знаком, расширение знака и нуля делает одно и то же).

Что делает cltq в сборке? имеет таблицу cbw / cwde / cdqe и эквивалентная инструкция movsx, а также то, что делает cwd / cdq / cqo (и эквивалентный mov / sar).

Ничто из перечисленного не является тем, что вы хотите, прежде чем unsigned div: используйте xor edx,edx для обнуления DX, вход старшей половины для 32/16 => 16-битного деления.

См. Также Когда и почему мы подписываем расширение и используем cdq с mul / div?


Чтобы избежать ложных зависимостей от записи частичных регистров, на большинстве последних процессоров наиболее эффективным было бы сделать загрузку movzx, просто чтобы получить 16-битное значение в AX без слияние с предыдущим значением RAX / EAX. Точно так же обнуление по xor (обычно?) Не распознается как идиома обнуления в частичных регистрах, поэтому вам нужен 32-битный размер операнда, даже если вы собираетесь читать только младшую половину

   movzx eax, word [wNum2]      ; zere extend only to avoid false dep from merging into EAX
   xor   edx, edx               ; high half dividend = DX = 0
   div   word [wNum3]
   mov   [wAns16], dx           ; store remainder from DX, not EDX

Ваш код, хранящий 32-битный EDX в [wAns16], по-видимому, является ошибкой, при условии, что там есть только 2 байта, прежде чем вы наступите на то, что последует за ним.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...