День 20. Различные команды языка ассемблера и оптимизация кода
Автор статьи никого не призывает к правонарушениям и отказывается нести ответственность за ваши действия. Вся информация предоставлена исключительно в ознакомительных целях. Все действия происходят на виртуальных машинах и внутри локальной сети автора. Спасибо!
Оглавление цикла заметок о языке ассемблера
Вчера все же удалось запустить оконное приложение на языке ассемблер под 64-разрядную Windows 10. Сегодня продолжаю изучать команды ассемблера по книге Рудольфа Марека «Ассемблер на примерах».
Спустя час все же удалось написать Hello, World! под Windows 10 64-bit. Методом научного тыка и поисков в интернете, получился следующий код:
global main
extern printf
extern getch
extern ExitProcess
section .text
main:
push rbp
mov rbp, rsp
sub rsp, 32
lea rcx, [cMsg]
call printf
cMsg db 'Hello, world!',0xd, 0xa, 0
Теперь можно продолжить изучать команды ассемблера. При помощи XCHG
можно поменять местами значения двух регистров без временного регистра (не только Python так умеет a,b = b,a). Кстати, именно так реализован NOP
:
xchg eax,eax
Следующая команда – LEA
, которая используется в Hello, World
. Она вычисляет эффективный адрес второго операнда и сохраняет его в первом операнде (являющийся только регистром). Второй операнд должен быть заключен в квадратные скобки.
Строкой в языке ассемблера, как и в Си, является последовательность символов, заканчивающаяся нулевым байтом.
Команда NOP
предназначена для организации задержки между операциями чтения и записи.
В языке ассемблера доступны сдвиги и ротация. При сдвиге битов влево, теряется левый бит (старший). При сдвиге вправо – наоборот. При ротации бит не теряется, а переставляется на противоположную сторону.
Где же можно применить сдвиги? Это быстрый способ умножить или разделить целое число на степень двойки. Например, вместо умножения числа на 16, можно сдвинуть число на 4 бита влево. Вернемся к 16 битного процессору:
use16
org 100h
mov ax, 0x0002
shl ax, 2
mov ax,4C00h
int 21h
То есть мы умножаем число два на 2 ^ 2
(= 4) и получаем 8. Деление аналогично:
use16
org 100h
mov ax, 0x0002
shr ax, 1
mov ax,4C00h
int 21h
Вытолкнутый бит сохраняется в флаге CF
.
Псевдокоманды
Команды ассемблера – это символические имена машинных команд, обработка которых приводит к генерации машинного кода. Псевдокоманды управляют работой самого компилятора.
Для определения констант применяются ранее упомянутые псевдокоманды: DB
, DW
и DD
:
number db 0x01
Для резервирования памяти служат три директивы: RESB
(байт), RESW
(слово), RESD
(двойное слово):
number resb 4 ;резервируется 4 байта для переменной number
Псевдокоманда TIMES
повторяет следующую псевдокоманду указанное количество раз.
times 5 inc ax
То же самое, как 5 раз подряд написать inc ax
.
Псевдокоманда INCBIN
подключает двоичный файл. Например, можно упаковать графический файл вместе с исполняемым файлом.
Псевдокоманда EQU
определяет константу.
Оптимизация кода
Процессоры работают быстрее с регистрами, чем с операндами, размещенными в памяти. Между памятью и процессором размещены кэши различного уровня, которые хранят часто используемые блоки. Выравнивание данных по определенным адресам «освобождает» буферы, тем самым программа работает быстрее.
Скорость доступа к памяти можно увеличить, если размещать данные по адресам, кратным степени двойки. Это можно реализовать при помощи директивы ALIGN
. Аргументом является число, кратным которому требуется размещать данные.
Инициализировать счетчик можно следующим образом:
mov ax, 0
Такой вариант логичен, но выполнится медленнее, чем следующий вариант:
xor ax, ax
Если ее аргументы одинаковые, то результат будет равен нулю. Применяя этот вариант, стоит помнить о влиянии команды XOR
на флаги признаков.
Оптимизация арифметики
Несколько команд INC
работает быстрее одной ADD
. Поэтому
add ax, 3
Лучше заменить на:
inc ax
inc ax
inc ax
Опять же нужно помнить, что INC
не сохраняет флаг переноса.
Оптимизация сравнения
Чтобы проверить регистр на нулевое значение, необязательно использовать CMP
. Есть еще OR
и TEST
.
cmp ax, 0
Лучше так:
is_zero:
mov ax, 0x00FF
xor ax, ax
;inc ax
or ax, ax
jz is_zero
При помощи команды TEST
можно определить, является ли число отрицательным:
test ax, ax
js is_negative
Подведем небольшой итог. Основные команды и полезные способы оптимизации разобраны. Следующая глава будет повествовать о написании реальных примеров.