проблема в понимании инструкций mul & imul языка ассемблера
Я учусь 80386 от сборка ПК paul caurter
mul source
- если операнд имеет размер байта, он умножается на байт в AL регистрация и результат сохраняется в 16 бит AX.
хорошо.
- если источник 16-разрядный, он умножается на слово в AX и 32-разрядный результат хранится в DX: AX.
Вопрос 1: почему DX: AX ? Почему он не может хранить в EAX / EDX?
imul
- это действительно сбивает с толку
imul dest, source1
imul dest, source1, source2
alt текст http://img697.imageshack.us/img697/8976/imul.gif
у меня проблемы с пониманием таблицы.
Вопрос 2: во 2-й записи таблицы. Опять же, почему DX: AX. Почему не EAX или EDX?
Теперь рассмотрим следующий код фрагмент:
imul eax ; edx:eax = eax * eax
mov ebx, eax ; save answer in ebx
mov eax, square_msg ; square_msg db "Square of input is ", 0
call print_string ; prints the string eax
mov eax, ebx
call print_int ; prints the int stored in eax
call print_nl ; prints new line
Вопрос 3: его previsously сказал, что The notation EDX:EAX means to think of the EDX and EAX registers as one 64 bit register with the upper
32 bits in EDX and the lower bits in EAX.
таким образом, ответ также хранится в edx, верно? в приведенном выше коде мы не рассматривали EDX, мы просто ссылаемся на EAX
Как это все еще работает?
Q4: у меня проблема с остальными записями в таблице. в худшем случае результат умножения двух N разрядных чисел (n = 8/16/32 бит) составляет 2n бит. Почему его хранение результата двух 16/32 бит умножения приводит к регистру тот же самый размер?
5 ответов
Q1/Q2: набор инструкций x86 сохраняет свою 16-разрядную историю. При выполнении 16-битного умножения ответ сохраняется в DX: AX. Так оно и есть, потому что так было в 16-битной стране.
Q3: код, который вы показали, имеет ошибку, если вы пытаетесь вычислить квадрат числа больше 2^16, потому что код игнорирует высокие 32 бита результата, хранящегося в edx
.
Q4: я думаю, вы, возможно, неправильно читаете таблицу. 8-разрядные умножения хранятся в 16-разрядном результат; 16-разрядные умножения хранятся в 32-разрядном результате; 32-разрядные умножения хранятся в 64-разрядном результате. На какую конкретно линию вы ссылаетесь?
да ладно..
существует множество различных вариантов инструкции imul. И что еще хуже, они работают по-разному, если вы пишете 16-битный код против 32-битного кода.
вариант, на который вы наткнулись, - это 16-битное умножение. Он умножает регистр AX на все, что вы передаете в качестве аргумента imul, и сохраняет результат в DX:AX.
один 32-битный вариант работает как 16-битное умножение, но записывает регистр в EDX: EAX. Использовать это вариант все, что вам нужно сделать, это использовать 32-битный аргумент.
например:
; a 16 bit multiplication:
mov ax, factor1
mov bx, factor2
imul bx ; result in DX:AX
; a 32 bit multiplication:
mov eax, factor1
mov ebx, factor2
imul ebx ; result in EDX:EAX
в 32-битном коде вы также можете написать imul в форме трех операндов. Это делает его более гибким и легким в работе. В этом варианте вы можете свободно выбирать регистр назначения и аргументы, но результат будет только 32 бита. Этот вариант imul не существует для 16-битного кода btw...
mov eax, factor1
mov ebx, factor2
imul ecx, eax, ebx ; result in ecx
Q1/Q2: почему DX: AX ? Почему он не может хранить в EAX / EDX?
как говорили другие, это только для обратной совместимости. Оригинал (i)mul
инструкции от 16-битного x86, который пришел долго до появления 32-разрядного набора инструкций x86, поэтому они не могли сохранить результат в eax/edx с не было электронного регистра.
Вопрос 3: в выше кода мы не рассматривали EDX, мы просто ссылаемся на EAX, как это все еще работает?
вы ввели небольшие значения, которые не вызывают переполнения результата, поэтому вы не видели различий. Если вы используете достаточно большие значения (>=16 бит), вы увидите, что EDX != 0 и результат печати будет неверным.
Q4: как получилось, что он хранит результат двух 16/32 бит умножения в регистре одинакового размера себя?
дело не в том, что результат все тот же размер как операнды. умножение двух n-разрядных значений всегда приводит к 2n-разрядному значению. Но в imul r16, r/m16[, imm8/16]
и их 32/64-разрядные аналоги высокие N-разрядные результаты отбрасываются. Они используются, когда вам нужно только понизьте 16/32/64 бита результата (т. е. для расширяющегося умножения), или когда вы можете убедиться, что результат не переполнение.
- форма двух операндов - с этой формой целевой операнд (первый операнд) умножается на исходный операнд (второй операнд). Операнд назначения является регистром общего назначения, а исходный операнд-непосредственным значением, регистром общего назначения или хранилищем памяти. промежуточный продукт (в два раза больше входного операнда) усекается и сохраняется в месте назначения операнда.
- [... То же самое для формы трех операндов]
современные компиляторы в настоящее время почти исключительно используют мульти-операнд imul
как для знаковых, так и для беззнаковых умножений, потому что
-
нижние биты всегда одно и то же в обоих случаях, и в C умножение двух переменных генерирует результат одинакового размера (
int
xint
→int
,long
xlong
→long
...) которые подходятimul
операнды красиво. Единственный способ заставить компиляторы испускать один операндmul
илиimul
is использование типа в два раза больше размера регистра - это очень редко, чтобы увидеть умножение, где результат шире, чем размер регистра, как
int64_t a; __int128_t p = (__int128_t)a * b;
так один операнд(i)mul
редко - расчет только нижние биты будут быстрее получение полного результата.
- гораздо больше гибкости в использовании из-за различных форм
imul
инструкция- в форме 2-операнда вам не нужно сохранять / восстанавливать EDX и EAX
- форма 3-операнда далее позволяет выполнять неразрушающее умножение
-
современные процессоры часто оптимизируются для мульти-операндных версий
imul
(поскольку современные компиляторы в настоящее время почти исключительно используется мульти-операндimul
как для знаковых, так и для беззнаковых умножений) так они будут быстрее, чем один операнд(i)mul
Q1 / Q2: я думаю, что причина историческая. До 32-разрядный вариант, нет еах или edX. 32-разрядная функциональность была добавлена для обратной совместимости.
Q3: биты низкого порядка будут находиться в eax. Это единственные, о которых вы заботитесь, если нет переполнения в высокие биты.
Q4: определенно нечетная таблица. Но я думаю, ты понимаешь.
A1: mul
первоначально присутствовал на процессорах 8086/8088/80186/80286, которые не имели регистров E** (E для расширенных, т. е. 32-разрядных).
А2: См. A1.
поскольку моя работа в качестве программиста ассемблерного языка перешла в семейство Motorola 680x0 до того, как эти 32-битные Intels стали обычным явлением, я остановлюсь на этом : -)