Другие языки программирования и технологии

///маленький вопрос АССЕМБЕРУ\\\

Для чего и в каких случаях ипользуют команду CDQ.
Приведите пример, с пояснением, пожалуйста.
Трам Тарарам
Трам Тарарам
2 194
Чтобы понять что делает и зачем применяется команда CDQ, нужно чётко представлять, как процессор производит деление. Пока неважно знаковое или беззнаковое.
Например, нужно разделить число 765 (2FDh) на 100 (64h). Загружаем значения в регистры:
mov eax,2FDh; (765) в скобках буду писать десятичные значения
mov ecx,64h; (100)
div ecx
Что мы ожидаем получить в результате деления? Правильно, число 7.
Смотрим содержимое регистра еах. Там неожиданно оказывается число 28F5C30h (42949680). Почему так произошло? Потому что процессор делит не содержимое регистра еах, а содержимое пары регистров edx:eax. Частное от деления записывается в регистр еах, а остаток от деления в edx.
Если вернуться на шаг назад до команды деления и посмотреть содержимое этих регистров, то выяснится, что edx = 00000001, eax = 000002FD. И на самом деле произошло деление не 765 на 100, а 00000001000002FDh (4294968061) на 100, что и дало полученный результат. Откуда взялась единица в edx? Да мало ли: результат выполнения каких-то предыдущих команд. Там может оказаться совершенно произвольное значение. Что делать, чтобы деление выполнялось правильно? Очевидно, что перед делением нужно обнулить регистр edx командами mov edx,0 или xor edx,edx. Можно и sub edx,edx.
mov eax,2FDh; (765)
mov ecx,64h; (100)
xor edx,edx
div ecx
Смотрим, что получилось: eax = 00000007 (7), edx = 00000041 (65) – остаток от деления 765 на 100. Всё правильно.
Теперь переходим к знаковому делению. Для положительных чисел поведение команды idiv ничем не отличается от div.
А с отрицательными есть особенности. Например, разделим -852 тоже на 100.
mov eax,0FFFFFCACh; (дополнительный код числа -852)
mov ecx,64h; (100)
xor edx,edx; мы ведь помним, что это старшая часть делимого
idiv ecx
Смотрим что получилось: eax = 028F5C20 (42949664), edx = 0000002C (44).
Откуда это взялось? Посмотрим, что мы делили:
edx=00000000, eax=FFFFFCAC. Т. е. разделилось на 100 число 00000000FFFFFCAC (4294966444) – положительное. А как разделить отрицательное -852? Нужно получить его 64-разрядный дополнительный код: FFFFFFFFFFFFFCAC. Теперь понятно, что перед делением отрицательных чисел в edx нужно записывать не ноль, а FFFFFFFF. Чтобы автоматически записывать в edx требуемое значение, нужно анализировать знаковый разряд делимого:
mov ax,0FFFFFCACh; (-852)
mov ecx,64h; (100)
xor edx,edx; в edx записан ноль
or eax,eax; устанавливаем флаги в зависимости от содержимого eax
jns M1; переход на М1, если число не отрицательное
not edx; инверсия содержимого edx (FFFFFFFF), если еах отрицательное
M1: idiv ecx
Результат: eax=FFFFFFF8 (доп. код числа -8)
edx=FFFFFFCC (доп. код -52 остаток от деления) Теперь всё правильно.

Так что же делает команда CDQ. Расширяет 32-разрядное значение до 64 разрядов с учётом знака. Т. е. то, что в примере делалось «вручную» : она копирует знаковый разряд из регистра еах во все 32 разряда регистра edx. Для положительного делимого edx обнуляется, для отрицательного – заполняется единицами. И всё нагромождение команд выше превращается в:
mov ax,0FFFFFCACh; (-852)
mov ecx,64h; (100)
cdq; <- это она ))
idiv ecx

P.S. С делением вообще бывает интересно. Например, можно поймать переполнение, если частное целиком не помещается в один регистр. :)
СШ
Сергей Шкуть
51 590
Лучший ответ
Шамс Смаш Спасибо вам большое за такой развернутый ответ! Хоть и дан он был 5 лет назад - до сих пор помогает.
Преобразование двойного слова (EAX) в учетверенное слово (EDX:EAX).
mov eax,12345678h
cdq
Сергей Балла
Сергей Балла
8 379
Трам Тарарам а почему знаковое деление без cdq не работает?