NASM. Почему код странно себя ведет?
(NASM, Win64, ld linker, Windows 11)
Почему этот код возвращает 5 как exit code, но если закомментировать 'mul R14', то он начинает возвращать 4?
global _start
section .text
_start:
mov rcx, 5
mov rdx, 1
call writeRow
writeRow:
mov R15, rcx
sub R15, rdx
mov R14, rdx
mov rax, 2
mul R14
xor rax, rax
mov rax, R15
ret
Ваш код производит некорректный результат из-за неудачной комбинации инструкций. Давайте разберем проблему:
1. Ваш код возвращает 5 как exit code иначе, если закомментировать 'mul R14', то он начинает возвращать 4 потому что:
- Вы умножаете значение в R14 на 2 с использованием `mul R14`. Это дает вам неправильный результат, поскольку умножение на 2 просто увеличивает значение в R14 в два раза.
- Когда вы не используете `mul R14`, то R14 не изменяется после `mov R14, rdx`, поэтому R15 - R14 равняется 5-1 = 4 и возвращается корректный результат.
2. Чтобы исправить код и вернуть верный результат, вы можете удалить `mul R14` и заменить его на простое сложение. Попробуйте следующий исправленный код:
```assembly
global _start
section .text
_start:
mov rcx, 5
mov rdx, 1
call writeRow
mov rax, 60 ; Системный вызов exit
xor rdi, rdi ; Код возврата 0
syscall
writeRow:
mov r15, rcx
sub r15, rdx
mov r14, rdx
add r15, r14 ; Заменяем 'mul R14' на 'add R15, R14'
mov rax, r15 ; Помещаем R15 в rax для возврата
ret
```
Это исправленный код, который должен возвращать правильный результат 4 как exit code. Попробуйте исправленный код и убедитесь, что он работает правильно.
Потому что после выполнения mul команда результат умножения устанавливается в RAX и RDX. После чего происходит обнуление RAX и запись в RAX с R15.
Ну в угол поставьте, не будет больше себя так вести.
Проблема здесь в том, что после выполнения инструкции `mul R14`, результат умножения сохраняется в регистрах `RDX:RAX`, и он может повлиять на последующие вычисления или операции с регистрами.
Когда вы умножаете `R14` (который содержит `1` в вашем случае) на `2`, результатом будет `2`, который сохраняется в регистре `RAX`, но также в регистре `RDX` останется остаток от умножения, который равен `0` в данном случае. После этого, когда вы обнуляете `RAX` и загружаете в него `R15`, он берет значение `RDX:RAX`, что приводит к тому, что в `RAX` будет значение `2`. Именно поэтому возвращается `2`.
Когда вы комментируете `mul R14`, `RDX:RAX` остается нетронутым, поэтому результат вычисления сохраняется только в `RAX`, и вы получаете ожидаемый результат `4`.
Чтобы избежать этой проблемы, вам нужно явно обнулить регистр `RDX` после выполнения `mul R14`, чтобы избавиться от остатка от деления:
global _start
section .text
_start:
mov rcx, 5
mov rdx, 1
call writeRow
writeRow:
mov R15, rcx
sub R15, rdx
mov R14, rdx
mov rax, 2
mul R14
xor rdx, rdx ; Обнуляем RDX
xor rax, rax
mov rax, R15
ret
```
Теперь код должен возвращать ожидаемые значения.
Код возвращает разные коды выхода (exit codes) в зависимости от того, выполняется ли инструкция mul R14, потому что эта инструкция влияет на регистр RAX, который используется для передачи кода выхода операционной системе в Windows.
Разберем подробнее:
1. Код выхода в Windows: В Windows код выхода программы передается через регистр RAX при выходе из программы.
2. Инструкция mul R14: Эта инструкция умножает значение в регистре RAX на значение в регистре R14 и сохраняет результат в RAX. В вашем коде R14 содержит значение 1 (из mov R14, rdx), поэтому mul R14 по сути дублирует значение RAX.
3. Изменение RAX:
o Без mul R14: После xor rax, rax регистр RAX обнуляется. Затем в него записывается значение из R15 (которое равно 4). Поэтому код выхода будет 4.
o С mul R14: После mul R14 значение в RAX удваивается. Затем xor rax, rax обнуляет RAX. После этого в него записывается значение из R15 (которое равно 4), и далее это значение удваивается инструкцией mul R14. В итоге код выхода получается 8.
Почему вы видите 5 вместо 8?
Возможно, вы используете какую-то обертку или инструмент для запуска кода, который модифицирует код выхода.
Рекомендации:
• Для ясности и избежания подобных побочных эффектов, лучше использовать отдельный регистр для хранения кода выхода и не модифицировать RAX после того, как в него будет записан код выхода.
• Вместо mul R14 используйте инструкцию mov rax, R15 для копирования значения из R15 в RAX, если вам нужно передать значение 4 в качестве кода выхода.
Исправленный код:
global _start
section .text
_start:
mov rcx, 5
mov rdx, 1
call writeRow
mov rax, R15 ; Код выхода 4
ret
writeRow:
mov R15, rcx
sub R15, rdx
ret