Объяснение ассемблера на сишных примерах
Основной ассемблерной командой является команда пересылки данных MOV, которую можно уподобить оператору присвоения. c = 0x333 на языке ассемблера записывается так: MOV EAX, 333h (обратите внимание на разницу записи шестнадцатеричных чисел!). Можно так же записать MOV EAX, EBX
(записать в регистр EAX значение регистра EBX).
Указатели заключаются в квадратные скобки. Сишное a = *b
на ассемблере записывается так: MOV EAX, [EBX]. При желании, к указателю можно добавить смещение: a = b[0x66]
эквивалентно: MOV EAX, [EBX + 0x66].
Переменные объявляются директивами DB (переменная в один байт), DW (переменная в одно слово), DD (переменная в двойное слово) и т.д. Знаковость переменных при их объявлении не указывается. Одна и та же переменная в различных участках программы может интерпретироваться и как число со знаком и как число без знака. Для загрузки переменной в указатель применяется либо команда LEA, либо MOV с директивой offset. Покажем это на следующем примере:
LEA EDX,b ;// регистр EDX содержит указатель на переменную b
MOV EBX,a ;// регистр EBX содержит значение переменной a
MOV ECX, offset a // регистр ECX
содержит указатель на переменную a
MOV
[EDX],EBX ;// скопировать переменную a b
MOV b, EBX ;// скопировать переменную a b
MOV b, a ;// !!!ошибка!!! так делать нельзя!!!
;// оба аргумента команды MOV не могут быть в памяти!
a
DD 66h;// объявляем переменную a типа двойного слова и инициализируем ее числом 66h
b
DD ? ;// объявляем неинициализированную переменную b
типа двойного слова
Листинг 1 основные типа пересылок данных
Теперь перейдем к условным переходам. Никакого "if" на ассемблере нет и эту операцию приходится осуществлять в два этапа. Команда CMP позволяет сравнить два числа, сохраняя результат своей работы во флагах. Флаги — это биты специального регистра, описание которого заняло бы слишком много места и поэтому здесь не рассматривается. Достаточно запомнить три основных состояния: меньше (bellow или less), больше (above или great) и равно (equal). Семейство команд условного перехода Jxx проверяют условие xx и, если оно истинно, совершают прыжок по указанному адресу. Например, JE прыгает, если числа равны (Jump if Equal), а JNE если неравны (Jump if Not Equal). JB/JA работают с беззнаковыми числами, а с JL/JG — со знаковыми. Любы два не противоречащих друг другу условия могут быть скомбинированы друг с другом, например: JBE — переход если одно беззнаковое число меньше другого или равно ему. Безусловный переход осуществляется командой JMP.
Конструкция CMP/ Jxx больше всего похожа на Бейсковское IF xxx GOTO, чем на Си. Вот несколько примеров ее использования:
CMP EAX, EBX ;// сравнить EAX
и EBX
JZ xxx ;// если они равны переход на xxx
CMP
[ECX], EDX ;// сравнить *ECX
и EDX
JAE yyy ;// если беззнаковый *ECX
>= EDX
перейти на yyy
Листинг 2 основные типы условных переходов
Вызов функций на ассемблере реализуется намного сложнее, чем на Си. Во-первых, существует по меньшей мере два типа соглашений — Си и Паскаль. В Си-соглашении параметры в функцию передаются справа налево, а из стека их вычищает вызывающий функцию код. В Паскаль соглашении все происходит наоборот! Аргументы передаются слева направо, а из стека из вычищает сама функция. Большинство API-функций операционный системы Windows придерживаются комбинированного соглашения stdcall, при котором аргументы заносятся в соответствии с Си-соглашением, а из стека вычищаются по соглашению Паскаль. Возвращаемое функцией значение помещается в регистр EAX или (для передачи 64-разрядных значений используется регистровая пара EDX:EAX). Разумеется, этих соглашением необходимо придерживаться только при вызове внешних функций (API, библиотек и т. д.)."Внутренние" функции им следовать не обязаны и могут передавать аргументы любым мыслимым способом, например, через регистры.
Вот простейший пример вызовафункции:
PUSH offset LibName ;// засылаем в стек смещение строки
CALL LoadLibrary ;// вызов функции
MOV h, EAX ;// EAX содержит возращенное значение
Листинг 3 вызов API-функции операционной системы