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