Партнерка на США и Канаду по недвижимости, выплаты в крипто
- 30% recurring commission
- Выплаты в USDT
- Вывод каждую неделю
- Комиссия до 5 лет за каждого referral
VOID ExitProcess(
UINT uExitCode // exit code for all threads
);
uExitCode - это значение никогда не используется и всегда равно 0.
Первая программа.
Теперь я приведу вам программу, которая выводит легендарное сообщение "Hello, World!".
.386
.model flat
extrn ExitProcess:PROC
extrn MessageBoxA:PROC
.data
Ttl db "First ASSEMBLER program",0h
Msg db 'Hello, World!!!!',0h
.code
start:
push 0h
push offset Msg
push offset Ttl
push 0h
call MessageBoxA
push 0h
call ExitProcess
end start
Мы вызываем функцию MessageBoxA, для вывода окна сообщения потом выходим из процесса. Теперь поподробнее.
Определение функции MessageBox из MS SDK в оригинале:
int MessageBox(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
);
Мы указали только модель памяти и модель вызов подпрограмм не указали, так как это просто не к чему. В качестве первого параметра мы передаём ноль, поэтому у окна сообщения не будет родительского окна. В качестве параметров lpText и lpCaption мы передаём адреса соответствующих строк, заметьте, что мы передаём адрес строки (т. е. её первого символа) у которой в конце стоит символ #0, это нужно, для того чтобы функция смогла найти конец строки. Последний параметр определяет стиль окна сообщения, в данном случае мы передали 0, что обозначает, что там будет только одна кнопка OK, иначе этот стиль обозначается MB_OK. Функцию ExitProcess вы уже знаете. После директивы end мы указали метку, с которой начнётся выполнение ваша программа.
Зачем нужна буква A в конце функции?
В Win32 есть 2 типа функции заканчивающиеся на A и на W. Всё отличие в строках, которые принимают функции. А-функции принимают ANSI строки. W-функции принимают Unicode строки. У каждой функции, которая принимает строки, есть обе версии. Обычно все пользуются ANSI версиями функций. Но в любом случае при вызове ANSI функции все строки сначала преобразуются в Unicode строки, и будет вызвана Unicode функция.
Компиляция.
Чуть-чуть не забыл рассказать, как надо компилировать программы. Для этого вам надо написать текст программы на ассемблере и сохранить в файл с расширением *.asm. Потом надо ассемблировать его. Для этого вам понадобится программа tasm32.exe она нахидтся в папке %tasmdir%\bin\tasm32.exe, формат её вызова такой: TASM [options] source [,object] [,listing] [,xref] (доп сведения смотрите в tasm32 /?) для большинства программ ассемблирование проходит так:
%tasmdir%\BIN\tasm32 /m3 /ml asmfile,,;
Где asmfile - имя вашего файла БЕЗ расширения.
Потом будет создан объектный файл, файл листинга и т. д. Теперь вам надо всё это слинковать. Для Линковки вам понадобится программа tlink32.exe. Вот формат её вызова:
TLINK objfiles, exefile, mapfile, libfiles, deffile, resfiles
Для всех программ в этом туториале линковка будет такой:
%tasmdir%\BIN\tlink32 /Tpe /aa asmfile, asmfile,,%tasmdir% \Lib\import32.lib
Где asmfile - имя вашего файла БЕЗ расширения.
Статическая библиотека %tasmdir% \Lib\import32.lib содержит в себе все функции библиотек kernel32.dll, user32.dll, gdi32.dll и может быть других стандартных библиотек. Желательно чтобы asmfile находился в папке %tasmdir%.
Для быстрой компиляции я вам советую создать *.bat файл, который всё это делает за вас. Вот текст моего файла.
Compile. bat
cls
echo off
d:\tasm_5\BIN\tasm32 /m3 /ml %1,,;
d:\tasm_5\BIN\tlink32 /Tpe /aa %1,%1,,d:\TASM_5\Lib\import32.lib
echo "Succesful"
Теперь после вызова
Compile sample
У вас готовый к выполнению экзешник.
Урок 4
Секции .data, .data? и. const нужны для определения данных программы. Место под данные резервируется с помощью директив db, dw, dd, dq, dt.
db - 1 байт
dw - 2 байта - 1 слово
dd - 4 байта - 2 слова
dq - 8 байт - 4 слова
dt - 10 байт
Секция .data наиболее универсальная мы резервируем память под данные и сразу же инициализируем их, т. е. задаём им начальные значения. Все данные из этой секции включаются в исполнимый файл. Секция. data? менее гибкая, так как данные нельзя инициализировать. Все данные в этой секции не включаются в исполнимый файл, поэтому место только резервируется, но начальные значения не задаются. Данные в обеих секциях имеют полный режим доступа. Секция. const предназначена только чтения. Но ошибок не возникает при попытке изменить эти данные (????). Эта секция самая бесполезная.
.data
Perem dd 0000FF11h
X1 dw 01235h
Binary db b
dd h
decimal dw 28d
large dq ABCDEFh
dw b
.data?
Perem1 dd?
Perem2 dq?
Perem5 dw?
Dd ?
Dw ?
Perem4 db?
..const
Const1 dd h
dw 768d
Строки.
В ассемблере можно задавать только ANSI строки, Unicode строки сложнее задавать и для их обработки существует целый ряд API функций. В ассемблере также можно вместо присваивания однобайтовой переменной некоторого числа можно присвоить переменно букву. Но, в конечном счете, эта переменная будет равна коду буквы в кодировке ANSI. При инициализации символа можно использовать и кавычки и апострофы - без разницы.
.data
Char1 db 'W'
Char2 db 'Й'
db "r"
Всё выше написанное тоже самое что и :
.data
Char1 db 57h
Char2 db 0C9h
db 72h
При объявлении строк можно просто написать стоку после директивы db. Это воспринимается как последовательность символов
String db "ASM"
Тоже самое:
String db 'A'
Db "S"
Db "M"
Тоже самое:
String db 41h
Db 53h
Db 4Dh
Тоже самое:
String db 41h, 53h, 4Dh
При передаче строк функциям в качестве параметров надо чтобы в конце строки был 0, для того чтобы функция смогла найти конец строки.
String db "ASM",0
Msg db "First ASSEMBLER program",0
Ttl db 'Hello, World!!!!',0
Заполнение данными.
Иногда нужно описать много одинаковых переменных примерно штук 30. Вы будете делать так
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; 30 раз
Это неудобно и некрасиво, тем более можно обсчитаться. Для сделана директива DUP.
db 30 DUP (0) ; тот же результат
в скобках указываем, чем надо заполнять, можно использовать символы в кавычках, обязательно чтобы размер в скобках совпадал с директивой.
Примеры:
Dd 10 DUP ("в")
Dw 45h DUP (0DF23h)
Dd 100b DUP (h)
С данными всё понятно.
Урок 5
Секции .data, .data? и. const нужны для определения данных программы. Место под данные резервируется с помощью директив db, dw, dd, dq, dt.
db - 1 байт
dw - 2 байта - 1 слово
dd - 4 байта - 2 слова
dq - 8 байт - 4 слова
dt - 10 байт
Секция .data наиболее универсальная мы резервируем память под данные и сразу же инициализируем их, т. е. задаём им начальные значения. Все данные из этой секции включаются в исполнимый файл. Секция. data? менее гибкая, так как данные нельзя инициализировать. Все данные в этой секции не включаются в исполнимый файл, поэтому место только резервируется, но начальные значения не задаются. Данные в обеих секциях имеют полный режим доступа. Секция. const предназначена только чтения. Но ошибок не возникает при попытке изменить эти данные (????). Эта секция самая бесполезная.
.data
Perem dd 0000FF11h
X1 dw 01235h
Binary db b
dd h
decimal dw 28d
large dq ABCDEFh
dw b
.data?
Perem1 dd?
Perem2 dq?
Perem5 dw?
Dd ?
Dw ?
Perem4 db?
..const
Const1 dd h
dw 768d
Строки.
В ассемблере можно задавать только ANSI строки, Unicode строки сложнее задавать и для их обработки существует целый ряд API функций. В ассемблере также можно вместо присваивания однобайтовой переменной некоторого числа можно присвоить переменно букву. Но, в конечном счете, эта переменная будет равна коду буквы в кодировке ANSI. При инициализации символа можно использовать и кавычки и апострофы - без разницы.
.data
Char1 db 'W'
Char2 db 'Й'
db "r"
Всё выше написанное тоже самое что :
.data
Char1 db 57h
Char2 db 0C9h
db 72h
При объявлении строк можно просто написать стоку после директивы db. Это воспринимается как последовательность символов
String db "ASM"
Тоже самое:
String db 'A'
Db "S"
Db "M"
Тоже самое:
String db 41h
Db 53h
Db 4Dh
Тоже самое:
String db 41h, 53h, 4Dh
При передаче строк функциям в качестве параметров надо чтобы в конце строки был 0, для того чтобы функция смогла найти конец строки.
String db "ASM",0
Msg db "First ASSEMBLER program",0
Ttl db 'Hello, World!!!!',0
Заполнение данными.
Иногда нужно описать много одинаковых переменных примерно штук 30. Вы будете делать так
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; 30 раз
Это неудобно и некрасиво, тем более можно обсчитаться. Для сделана директива DUP.
db 30 DUP (0) ; тот же результат
в скобках указываем, чем надо заполнять, можно использовать символы в кавычках, обязательно чтобы размер в скобках совпадал с директивой.
Примеры:
Dd 10 DUP ("в")
Dw 45h DUP (0DF23h)
Dd 100b DUP (h)
С данными всё понятно.
Урок 6
Одно из преимуществ ассемблера заключается в использовании символьных меток и меток данных (переменных). По сути оба типа меток - это символьные метки. На этом уроке я вам объясню, как надо пользоваться символьными метками и метками данных. Начну с более сложного - меток данных. В секции с данными с помощью директив db, dw, dd, dq вы резервируете память под данные соответствующего размера. Но надо неким образом получить доступ к этим данным. Для этого существуют символьные метки переменных. По существу метка возвращает смещение, на котором она находится. Метка виртуальна - в шестнадцатеричных кодах у неё нет эквивалента, она создана только для удобства программиста. При использовании метки под ней подразумевается некоторое смещение, на которую она указывает.
Пример:
.data
PARAM dd h
.code
mov eax, [PARAM]
mov [PARAM], ebx
По определению это правильный метод использования меток. После данных операций регистр eax будет равен . Но парадокс у операций:
mov eax, PARAM
mov PARAM, ebx
тот же самый результат и никаких ошибок при компиляции не возникает. Самое главное чтобы совпадали размеры. Но мой вам совет лучше всегда пользуйтесь квадратными скобками. Примеры
.data
PARAM dw 1122h
PARAM1 db 12h
PARAM2 dd h
.code
mov eax,0
mov ebx, 0
mov ecx,0
mov edx, 0
mov al, byte ptr [PARAM]
mov bl, [PARAM1]
mov ecx,[PARAM2]
mov dx, word ptr [PARAM2]
mov bh, byte ptr [PARAM2]
После всех манипуляций состояние регистров будет таким:
AL = 22
BL = 12
ECX =
DX = 5678
BH = 78
Ещё один пример:
.data
PARAM1 dd h
.code
mov ax, 8888h
mov word ptr [PARAM1], ax
mov ebx, [PARAM1]
mov ecx, 0
mov edx, 0
mov cx, word ptr [PARAM1+2]
mov [PARAM1], 089ABCDEFh
mov dx, word ptr [PARAM1]
После всех манипуляций состояние регистров будет таким:
EBX =
CX = 1234
DX = CDEF
Думаю здесь всё понятно. Размер копируемых данных по умолчанию равен размеру директивы, которая стоит после метки.
Символьные метки отличаются от меток данных тем, что их можно описывать в любом месте программы. После символьной метки надо ставить двоеточие (как в других языках высокого уровня). Они также возвращают смещение, на котором стоят. Они используются при прыжках (о них расскажу позже), или с командой call. Команда call передаёт управление команде, которая находится на смещении, на которое указывает данная метка. Их также можно использовать вместо меток данных. Но при получении значения или его изменении надо обязательно указывать размер данных.
Пример:
.data
metka:
PARAM1 dd h
.code
mov ecx, 0
mov ebx, 0
mov ax, 0BEDAh
mov word ptr [metka],ax
mov bx, word ptr [metka]
mov cx, word ptr [metka+2]
После этих манипуляций состояние регистров будет такое:
CX = 1234
BX = BEDA
Получение смещения метки.
Для получения смещения метки есть специальный префикс offset. Вы, наверное, видели использование этого префикса в нашей первой программе. Вот пример использования префикса offset:
.data
PARAM1 dd h
.code
mov eax, offset PARAM1
После этого в регистре eax будет находиться смещение метки PARAM1. При использовании префикса назначением также может быть ячейка памяти, у следующих команд будет тот же результат что и в первом случае:
mov [PARAM1], offset PARAM1
mov eax, [PARAM1]
Альтернативой префиксу offset является команда lea. Пример:
.data
metka:
PARAM1 dd h
.code
lea eax, metka
После этого в регистре eax будет находиться смещение метки metka. В команде lea можно получать смещение метки данных. Следующий пример будет иметь тот же результат что и предыдущий:
.data
metka:
PARAM1 dd h
.code
lea eax, PARAM1
Урок 7
Для корректной обработки данных в ассемблере нужна разветвленность программы. Это достигается использованием условных переходов. Условный переход это такая команда процессору, при которой в зависимости от состояния регистра флагов производится передача управления по некоторому адресу иначе говоря прыжок. Этот адрес может быть ближним или дальним. Прыжок считается ближним, если адрес, на который делается прыжок, находится не дальше чем 128 байт назад и 127 байт вперёд от следующей команды. Дальний прыжок это прыжок дальше, чем на [-128,127] байт. Прыгать просто так в любом месте программы бессмысленно. Каждая команда изменяет регистр флагов в зависимости от результата своей операции. Обычно перед командой прыжка идёт команда сравнения, которая изменяет регистр флагов в зависимости от результата. Команд сравнения две: cmp и test. Наиболее универсальная cmp. О команде test я вам расскажу позже.
Команда Cmp производит сравнение двух операндов. Она сравнивает два значения (регистр, память, непосредственное значение) и устанавливает флаг нуля Z (zeroflag) если они равны.
Cmp eax, ebx
Cmp edx, h
Cmp [ebx], esi
Cmp byte ptr [VALUE1], 045h
Регистр флагов.
Регистр флагов имеет набор флагов, которые устанавливаются или сбрасываются в зависимости от вычислений или других событий. Я не буду расказывать о всех, а только о некоторых важных:
ZF (Zero flag, Флаг нуля) - Этот флаг устанавливается, когда результат вычисления нулевой (чтобы сравнить - фактически substraction без того, чтобы сохранить(экономить) результаты, но устанавливать флажки только). This flag is set when the result of a calculation is zero (сравнение это фактически вычитание без сохранения результата, и установка соответствующих флагов).
SF (Sign flag, Флаг знака) - Если установлен, значит результат вычисления - отрицательное число.
CF (Carry flag, Флаг переноса - Флаг переноса, содержит крайний левый (старший) бит после вычислений.
OF (Overflow flag, Флаг переполнения - Указывает переполнение результата вычисления, т. е. результат больше, приемник.
Есть еще большое количество флагов (флаг паритета (pf), вспомогательный флаг переноса (af), флаг трассировки (tf), флаг прерывания (if), флаг управления (df), флаг уровня привилегий ввода/вывода (iopl) , флаг вложенности задачи (nt), флаг возобновления (rf), флаг виртуального режима (vm)) но так как мы не будем их использовать, я не буду о них рассказывать.
Прыжки.
Приведу пример самой распространённой команды условного перехода jz:
Cmp eax, 42h
Jz metka2
Если регистр eax равен 42, то управление передастся первой команде после метки metka2.
Также есть команда безусловного перехода - jmp. Она передаёт управление в любом случае. Вы скажете: "Для этого нужна команда call". Я отвечу: "Команда call пихает в стек адрес возврата, а jmp не трогает стек". Условные переходы могут делать только ближний прыжок, а команда jmp может делать и короткий и дальний прыжки.
Вот таблица всех условных переходов.
cmp x, y
Опкод | Значение(переход, если...) | Условие |
JA | Jump if above (X > Y) | CF=0 & ZF=0 |
JAE | Jump if above or equal (X >= Y) | CF=0 |
JB | Jump if below (X < Y) | CF=1 |
JBE | Jump if below or equal (X < Y) | CF=1 or ZF=1 |
JC | Jump if carry (cf=1) | CF=1 |
JCXZ | Jump if CX=0 | регистр CX=0 |
JE (то же, что и JZ) | Jump if equal (X = Y) | ZF=1 |
JG | Jump if greater (signed) (X > Y) | ZF=0 & SF=OF |
JGE | Jump if greater or equal (signed) (X >= Y) | SF=OF |
JL | Jump if less (signed) (X < Y) | SF!= OF |
JLE | Jump if less or equal (signed) (X <= Y) | ZF=1 or SF!=OF |
JMP | Безусловный переход | - |
JNA | Jump if not above (X <= Y) | CF=1 or ZF=1 |
JNAE | Jump if not above or equal (X < Y) | CF=1 |
JNB | Jump if not below (X >= Y) | CF=0 |
JNBE | Jump if not below or equal (X > Y) | CF=1 & ZF=0 |
JNC | Jump if not carry (cf=0) | CF=0 |
JNE | Jump if not equal (X!= Y) | ZF=0 |
JNG | Jump if not greater (signed) (X <= Y) | ZF=1 or SF!=OF |
JNGE | Jump if not greater or equal (signed) (X < Y) | SF!=OF |
JNL | Jump if not less (signed) (X >= Y) | SF=OF |
JNLE | Jump if not less or equal (signed) (X > Y) | ZF=0 & SF=OF |
JNO | Jump if not overflow (signed) (of=0) | OF=0 |
JNP | Jump if no parity (pf=0) | PF=0 |
JNS | Jump if not signed (signed) (sf=0) | SF=0 |
JNZ | Jump if not zero (X!= Y) | ZF=0 |
JO | Jump if overflow (signed) (of=1) | OF=1 |
JP | Jump if parity (pf=1) | PF=1 |
JPE | Jump if parity even ( | PF=1 |
JPO | Jump if parity odd | PF=0 |
JS | Jump if signed (signed) | SF=1 |
JZ | Jump if zero (X = Y) | ZF=1 |
Все команды перехода имеют один операнд: смещение для перехода.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 |


