call dest jmp dest
ret
Команду CBW можно заменить засылкой нуля, если расширяемое число положительное. Команду CDQ можно заменить засылкой нуля, если расширяемое число положительное, или парой команд MOV + SAR, если знак расширяемого числа не известен. Недостаток – команды XOR и SAR меняют флаги.
cdq xor edx, edx
cdq mov edx, eax
sar edx, 31
Вместо команд инкремента и декремента можно использовать команду LEA.
Сложение и вычитание с константой можно заменить командой LEA.
Вместо умножения и деления на степень числа 2 используйте сдвиги.
Умножение и деление на константу можно заменить командой LEA или сочетанием команд сдвига и команд сложения и вычитания.
Деление на константу можно заменить умножением на константу.
Обнуление регистров производится с помощью команды XOR.
xor eax, eax ; EAX = 0 при любом значении EAX, которое было до этой команды
Не используйте команду MOVZX для чтения байта – это требует 3 тактов для выполнения. Заменой может служить такая пара команд, выполняющаяся за 2 такта:
xor еах, еах
mov al, <источник>
Засылку непосредственного операнда в ячейку памяти можно производить через регистр – такие команды лучше спариваются.
mov x, 1 mov eax, 1
mov x, eax
mov [ebx], 1 mov eax, 1
mov [ebx], eax
Аналогично команды PUSH и POP, работающие с ячейкой памяти, можно заменить парой команд MOV + PUSH или POP + MOV.
push x mov eax, x
push eax
pop x pop eax
mov x, eax
8.3.4. Выравнивание
80-битные данные должны быть выравнены по 16-байтным границам (то есть четыре младших бита адреса должны быть равны нулю).
Восьмибайтные данные должны быть выравнены по восьмибайтным границам (то есть три младших бита адреса должны быть равны нулю).
Четырёхбайтные данные должны быть выравнены по границе двойного слова (то есть два младших бита адреса должны быть равны нулю).
Двухбайтные данные должны быть выравнены по границе слова.
Метки для переходов, особенно метки, отмечающие начало цикла, должны быть выравнены по 16-байтным границам.
Каждое невыравненное обращение к данным означает потерю тактов процессора.
Для выравнивания данных и кода используется директива ALIGN:
ALIGN <число>
Число должно быть степенью двойки. Данные и команда, расположенные после директивы ALIGN, будут размещены по адресу, кратному указанному числу.
Примеры
Процедура вычисления наибольшего общего делителя двух беззнаковых чисел. Для нахождения НОД используется алгоритм Евклида: пока числа не равны, надо вычитать из большего числа меньшее. Процедура получает параметры через регистры EAX и EDX и возвращает результат через регистр EAX.
NOD proc
N1: cmp eax, edx ; Сравниваем числа
je N3 ; Если числа равны, завершаем работу процедуры
ja N2 ; Если первое число больше, обходим обмен
; Поскольку команды перехода не меняют флаги, оба перехода
; выполняются или не выполняются по результатам одного сравнения
xchg eax, edx ; Если первое число было меньше, выполняем обмен
N2: sub eax, edx ; Вычитаем из большего числа меньшее
jmp N1 ; Переход к началу цикла
N3: ret
NOD endp
Ввод и вывод в консольном приложении. В программе используются следующие функции Win32 API.
SetConsoleTitle – меняет заголовок окна консоли. Получает один параметр – указатель на строку, которая будет выведена в заголовке. Строка должна заканчиваться нулём.
GetStrHandle – возвращает идентификатор устройства ввода, устройства вывода или устройства отчёта об ошибках. Для консольного приложения всё три устройства являются консолью, но идентификаторы будут разными. Функция получает один параметр – указание, идентификатор какого устройства нужно вернуть. Чтобы получить идентификатор устройства ввода, надо передать функции число -10, чтобы получить идентификатор устройства вывода – число -11, а чтобы получить идентификатор устройства отчёта об ошибках – число -12. Функция возвращает требуемый идентификатор через регистр EAX.
WriteConsole – выводит строку в консоль. Получает следующие параметры – идентификатор устройства вывода, адрес выводимой строки, количество символов для вывода, адрес переменной, куда будет записано количество выведенных символов, зарезервированный указатель.
ReadConsole – вводит строку из консоли. Получает следующие параметры – идентификатор устройства ввода, адрес памяти, куда будет записана введённая строка, максимальное количество читаемых символов, адрес переменной, куда будет записано реальное количество введённых символов, зарезервированный указатель.
Не забывайте, что параметры кладутся в стек, начиная с последнего, и что введённая строка всегда будет содержать в конце символы с кодами 13 и 10, которые появляются при нажатии на клавишу ВВОД (без чего, однако, ввод не завершится).
.686
.model flat, c
option casemap: none
include \masm32\include\windows. inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
str db 256 dup(0)
hStdIn dd 0
hStdOut dd 0
slength dd 0
.const
sConsoleTitle db 'Input and Output',0 ; Заголовок окна консоли. Заканчивается нулём
prompt db 'Input a string', 13,10 ; Приглашение для ввода. Символы с кодами 13 и 10
; обеспечивают перевод курсора на следующую строку
STD_INPUT_HANDLE equ -10d ; Определяем символические имена для констант,
STD_OUTPUT_HANDLE equ -11d ; указывающих требуемое устройство
.code
program:
; Вывод заголовка консоли
push offset sConsoleTitle ; Кладём в стек адрес начала строки заголовка консоли
call SetConsoleTitle ; Вызываем функцию
; Получаем идентификатор устройства ввода
push STD_INPUT_HANDLE ; Кладём в стек параметр функции GetStdHandle
call GetStdHandle ; Вызываем функцию
mov hStdIn, eax ; Сохраняем полученный идентификатор
; Получаем идентификатор устройства вывода
push STD_OUTPUT_HANDLE
call GetStdHandle
mov hStdOut, eax
; Выводим приглашение
push 0 ; Зарезервированный параметр, в стек кладём 0
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |


