×
Главная   »   Заметки   »   День 20. Различные команды языка ассемблера и оптимизация кода
День 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

Подведем небольшой итог. Основные команды и полезные способы оптимизации разобраны. Следующая глава будет повествовать о написании реальных примеров.

417 просмотров
07.06.2022
Автор