Партнерка на США и Канаду по недвижимости, выплаты в крипто
- 30% recurring commission
- Выплаты в USDT
- Вывод каждую неделю
- Комиссия до 5 лет за каждого referral
RECT struc
rcLeft UINT?
rcTop UINT?
rcRight UINT?
rcBottom UINT?
RECT ends
MB_OK = 0000H
MB_OKCANCEL = 0001H
MB_ABORTRETRYIGNORE = 0002H
MB_YESNOCANCEL = 0003H
MB_YESNO = 0004H
MB_RETRYCANCEL = 0005H
;=====[CUT HERE]===========
Думаю что с включаемыми файлами всё понятно. Директива include заменяется на всё содержимое включаемого файла.
Если мы укажем в директиве .model укажем модель вызова функций. То вызывать API будет намного проще
Call <функция>, <параметр1>,<параметр2>,<параметр3>
Теперь наша первая программа будет иметь такой вид:
.386
.model flat, stdcall
extrn MessageBoxA:PROC
extrn ExitProcess:PROC
.data
Msg db "First ASSEMBLER program",0h
Ttl db 'Hello, World!!!!',0h
.code
start:
call MessageBoxA,0,offset Msg, offset Ttl,0
call ExitProcess, 0
end start
Команда
call func, param1,pram2, paramn
при компиляции автоматически преобразуется в
Push paramn
Push param2
Push param1
Call func
Вот такая фишка. В MASM для этого уже есть специальный макрос invoke, но об этом позже.
Реализация конструкции if.
Если у вас есть опыт в языках программирования, возможно, вы видели что-то вроде if/else конструкций:
.IF eax==1
;eax равен 1
.ELSEIF eax=3
; eax равен 3
.ELSE
; eax не равен 1 и 3
.ENDIF
Эта конструкция очень полезна. Вам не нужно вставлять сравнения и переходы, а только вставте директиву. IF (не забудьте точку перед. IF и. ELSE и т. д.). Директива. endif нужна для определения ещё одного сравнения, если предыдущие сравнения были ложгыми. Инструкции после директивы. else выполняются только в том случае, если все сравнения были ложными. Вложенности if позволяются:
.IF eax==1
.IF ecx!=2
; eax= 1 и ecx не равно 2
.ENDIF
.ENDIF
Это может быть сделано проще:
.IF (eax==1 && ecx!=2)
; eax = 1 и ecx не равно 2
.ENDIF
А вот и операторы, которые вы можете использовать:
== | равно |
!= | не равно |
> | больше |
< | меньше |
>= | больше или равно |
<= | меньше или равно |
& | проверка бита |
! | инверсия ( NOT ) |
&& | логическое 'И' ( AND ) |
|| | логическое 'ИЛИ' ( OR ) |
CARRY? | флаг переноса (cf) установлен |
OVERFLOW? | флаг переполнения (of) установлен |
PARITY | флаг паритета (pf) установлен |
SIGN? | флаг знака (sf) установлен |
ZERO? | флаг нуля (zf) установлен |
Такая конструкция есть и в TASM и в MASM.
Урок 11
Биты, сдвиг логический, арифметический и циклический
Обратите внимание: Большинство примеров ниже использует 8 битные числа, это сделано для того, чтобы вам было легче понять, как это работает.
Функции сдвига (сдвиг логический операнда влево/вправо)
SHL операнд, количество_сдвигов
SHR операнд, количество_сдвигов
SHL и SHR сдвигают биты операнда (регистр/память) влево или вправо соответственно на один разряд.
Указанное выше действие повторяется количество раз, равное значению второго операнда.
Пример:
; al = (двоичное)
shr al, 3
Это означает: сдвиг всех битов регистра al на 3 разряда вправо. Так что al станет . Биты слева заполняются нулями, а биты справа выдвигаются. Последний выдвинутый бит, становится значением флага переноса cf.
Бит переноса это бит флагового регистра процессора. Этот регистр не такой как eax или ecx, к которому вы можете непосредственно обращаться (хотя есть опкоды, которые это делают), его содержание, зависит от результатов многих команд. Об этом я вам расскажу позже, единственное, что вы должны сейчас запомнить, это то, что флаг переноса это бит во флаговом регистре и что он может быть установлен (т. е. равен 1) или сброшен (равен 0).
Команда shl такая же, как и shr, но сдвигает влево.
; bl = (двоичное)
shl bl, 2
После выполнения команды регистр bl будет равен (двоичное). Два последних бита заполнились нулями, флаг переноса установлен, потому, что последний выдвинутый слева бит был равен 1
Здесь есть еще два других опкода: (сдвиг арифметический операнда влево/вправо)
SAL операнд, количество_сдвигов
SAR операнд, количество_сдвигов
Команда SAL такая же, как SHL, а вот SAR не совсем такая, как SHR. Команда SAR также, как и SHR сдвигает все биты операнда вправо на один разряд, при этом выдвигаемый справа бит становится значением флага переноса cf.
Обратите внимание: одновременно слева в операнд вдвигается не нулевой бит, как в SHR, а значение старшего бита операнда. Пример:
al =
sar al, 3
al =
sar al, 2
al =
bl =
sar bl, 3
bl =
Циклический сдвиг
rol операнд, количество_сдвигов ; циклический сдвиг операнда влево
ror операнд, количество_сдвигов ; циклический сдвиг операнда вправо
rcl операнд, количество_сдвигов ; циклический сдвиг операнда влево через флаг переноса
rcr операнд, количество_сдвигов ; циклический сдвиг операнда вправо через флаг переноса
Циклический сдвиг напоминает смещение, выдвигаемые биты, снова вдвигаются с другой стороны:
Пример: команды ror (циклический сдвиг вправо)

Как видно из рисунка выше, биты вращаются, то есть каждый бит, который выталкивается снова вставляется с другой стороны. Флаг переноса cf содержит значение последнего выдвинутого бита.
RCL и RCR сдвигают все биты операнда влево (для RCL) или вправо (для RCR) на один разряд, при этом старший(для RCL) или младший(для RCR) бит становится значением флага переноса cf; одновременно старое значение флага переноса cf вдвигается в операнд справа(для RCL) или слева(для RCR) и становится значением младшего(для RCL) или старшего(для RCR) бита операнда. Указанные действия повторяются количество раз, равное значению второго операнда.
ROL и ROR сдвигают все биты операнда влево(для ROL) или вправо(для ROR) на один разряд, при этом старший(для ROL) или младший(для ROR) бит операнда вдвигается в операнд справа(для ROL) или слева(для ROR) и становится значением младшего(для ROL) или старшего(для ROR) бита операнда; одновременно выдвигаемый бит становится значением флага переноса cf. Указанные действия повторяются количество раз, равное значению второго операнда.
Стековые операции.
Иногда при вызове некоторых подпрограмм нужно сохранить все регистры для корректной работы программы в дальнейшем. Для этого вы будете последовательно сохранять каждый регистр в стеке и извлекать точно также:
Push eax
Push ebx
Push ecx
[также все остальные регистры]
....
[вытаскиваем оттуда остальные регистры]
Pop ecx
Pop ebx
Pop eax
Такой подход очень громоздок именно для этого и создана команда pushad. Она сохраняет все расширенные регистры в стеке. Соответственно команда popad нужна для извлечения из стека всех регистров.
Pushad
........
Popad
Также есть команды для сохранения младших регистров pushaw и popaw. Они равносильны командам PUSH AX, CX, DX, BX, SP, BP, SI, DI и POP DI, SI, BP, BX, DX, CX, AX.
Иногда надо сохранять значения флагов после выполнения некоторой операции. Для этого нужны команды pushf и popf. Эти команды сохраняют в стеке значение регистра флагов или восстанавливают его оттуда.
Изменение флагов процессора.
- CLC- установка CF=0 CLD- установка DF=0 CLI - установка IF=0 LAHF- загрузка регистра AH из регистра флагов: устанавливает биты 7, 6, 4, 2 и 0 по значениям флагов SF ZF AF PF CF. SAHF- запись регистра AH в регистр флагов: устанавливает флаги SF ZF AF PF CF по битам 7, 6, 4, 2 и 0 регистра AH. STC - установка CF=1 STD - установка DF=1 STI - установка IF=1
Урок 12
Загрузка и выгрузка данных.
Команды lodsb, lodsw, lodsd нужны, для того чтобы загрузить байт (слово, двойное слово) из памяти, на которую указывает регистр ESI в регистр al, ax или eax. После присваивания значения регистру значение регистра ESI увеличивается на 1,2 или 4. Вот эквиваленты этих команд
Lodsb Mov al, byte ptr [esi]
Inc esi /dec esi
Lodsw Mov ax, word ptr [esi]
Add esi, 2h /sub esi, 2h
Lodsd Mov eax, dword ptr [esi]
Add esi, 4h / sub esi, 4h
Следовательно, эти команды облегчают нам обработку данных, сразу увеличивая значение регистра esi для следующей порции данных. Регистр может также уменьшаться в зависимости от флага направления DF. Если он выставлен то регистр будет уменьшаться, а если он сброшен, то регистр esi будет увеличиваться.
Абсолютно, противоположное делают команды stosb, stows и stosd. Они загружают регистр al, ax, eax в память, на которую указывает регистр edi. Изменение регистра edi зависит от того же самого флага направления DF.
stosb Mov byte ptr [edi], al
Inc edi /dec edi
stosw Mov word ptr [edi], ax
Add edi, 2h /sub edi, 2h
stosd Mov dword ptr [edi], eax
Add edi, 4h / sub edi, 4h
Команды movsb, movsw и movsd пересылают байт (слово, двойное слово) на который указывает esi туда, куда указывает edi с автоматическим увеличением/уменьшением этих регистров.
movsb Mov al, byte ptr [esi]
Mov byte ptr [edi], al
Inc edi /dec edi
Inc esi /dec esi
stosw Mov ax, word ptr [esi]
Mov word ptr [edi], ax
Add edi, 2h /sub edi, 2h
Add esi, 2h /sub esi, 2h
stosd Mov eax, dword ptr [esi]
Mov dword ptr [edi], eax
Add edi, 4h / sub edi, 4h
Add esi, 4h / sub esi, 4h
Сравнение данных.
Команды scasb, scasw и scasd сравнивают содержимое регистра al, ax, eax с содержимым памяти на которую указывает регистр edi. Разумеется с увеличением регистра edi. Более продвинутые команды cmpsb, cmpsw, cmpsd сравнивают значения памяти, на которые указывают регистры esi и edi соответственно. С соответствующим изменением регистра флагов.
Префикс rep.
Все вышеописанные команды предназначены удобно использовать в циклах, т. е. при повторяющихся операциях. Префикс rep нужен для повторения команды обработки блоков количество раз, которое указано в регистре ecx.
Пример:
Mov ecx, 045h
Mov edi, 0422310h
Mov al, 088h
Rep stosb
После этого память в диапазоне 0422310hh будет заполнена значениями 88h. Для команд cmps* и scas* префикс повторяет команду только до тех пор, пока флаг ZF установлен. Префиксы repe, repz это тоже самое. Если после очередного выполнения команды флаг ZF сбросился, то повторы прекращаются.
Не менее полезен префикс repne, он будет повторять команду пока флаг ZF сброшен. Если после очередного выполнения команды флаг ZF установился, то повторы прекращаются. То же самое делает префикс repnz.
Str1 db 'qwerty'
Str2 db '111111'
::
Mov ecx, 06h
Mov esi, offset str1
Mov edi, offset str2
Rep movsb
После всех этих манипуляций данные будут такими:
Str1 db 'qwerty'
Str2 db 'qwerty'
Ещё пример:
Str1 db 'qwerty'
Str2 db 'qwe111'
....
Mov ecx, 06h
Mov esi, offset str1
Mov edi, offset str2
Rep cmpsb
Это повторение закончится на 4 шаге при сравнении 'r' и '1' т. е. при сравнении 'r' и '1' флаг ZF сбросится и повторение прекратится. Тем самым можно сравнивать две строки а после Rep cmpsb проверять равен ли регистр ECX нулю, если он равен нулю, то строки равны.
Str1 db 'qwerty'
Str2 db '1111t1'
......
Mov ecx, 06h
Mov esi, offset str1
Mov edi, offset str2
Repne cmpsb
Это повторение прекратится на 5 шаге при сравнении 't' и 't' т. е. при их сравнении флаг ZF установится и повторение прекратится.
Урок 13
На всех предыдущих уроках я рассказывал о компиляторе TASM, потому что он стандартный и ненавороченный. Но более популярный компилятор это MASM, он создан специально для написания программ на ассемблере для Win32. В нём есть макросы и специальные директивы для упрощения программирования.
Функции.
Основное преимущество MASM это макрос invoke, он позволяет вызывать API функции по-обычному с проверкой количества и типа параметров. Это почти тот же call, как в TASM, но этот макрос проверяет количество параметров и их типы. Вот так вызывается функция:
Invoke <функция>, <параметр1>, <параметр2>, <\параметр3>
Чтобы использовать invoke для вызова процедуры, вы должны определить ее прототип:
PROTO STDCALL testproc:DWORD, :DWORD, :DWORD
Эта директива объявляет процедуру, названную testproc, которая берет 3 параметра размером DWORD.
Теперь, если вы сделаете это...
invoke testproc, 1, 2, 3, 4
...
masm выдаст вам ошибку потому, что процедура testproc берет 3 параметра, а не 4. Masm также имеет контроль соответствия типов, т. е. проверяет, имеют ли параметры правильный тип (размер).
В invoke вы можете использовать ADDR вместо OFFSET. Это сделает адрес в правильной форме, когда код будет собран.
testproc PROTO STDCALL :DWORD, :DWORD, :DWORD
.code
testproc proc param1:DWORD, param2:DWORD, param3:DWORD
....
ret
testproc endp
Это создает процедуру, названную testproc, с тремя параметрами. Прототип используется, invoke. Все параметры можно использовать в коде процедуры, они автоматически извлекутся из стека. Также в процедурах можно использовать локальные переменные.
testproc proc param1:DWORD, param2:DWORD, param3:DWORD
LOCAL var1:DWORD
LOCAL var2:BYTE
mov ecx, param1
mov var2, cl
mov edx, param2
mov eax, param3
mov var1, eax
add edx, eax
mul eax, ecx
mov ebx, var1
.IF bl==var2
xor eax, eax
.ENDIF
ret
testproc endp
Вы не можете использовать эти переменные вне процедуры. Они сохранены в стеке и удаляются при возврате из процедуры.
Конструкции сравнения и повтора .
If - Об этой конструкции я рассказывал на 10 уроке. Она имеет тот же самый синтаксис, что и в TASM.
Repeat - Эта конструкция выполняет блок, пока условие не истинно:
.REPEAT
; код здесь
.UNTIL eax==1
Эта конструкция повторяет код между repeat и until, пока eax не станет равным 1.
While - Конструкция while это инверсия конструкции repeat. Она выполняет блок, пока условие истинно:
.WHILE eax==1
; код здесь
.ENDW
Вы можете использовать директиву. BREAK, чтобы прервать цикл и выйти.
.WHILE edx==1
inc eax
.IF eax==7
.BREAK
.ENDIF
.ENDW
Если eax=7, цикл while будет прерван.
Директива continue осуществляет переход на код проверяющий условие цикла в конструкциях repeat и while.
Теперь наша первая программа видоизменяется следующим образом:
.486
.model flat, stdcall
option casemap :none
includelib .\masm32\lib\kernel32.lib
includelib .\masm32\lib\user32.lib
MessageBoxA PROTO STDCALL :DWORD, :DWORD, :DWORD, :DWORD
ExitProcess PROTO STDCALL :DWORD
.data
ttl db '11111',0
.code
start:
invoke MessageBoxA,0,offset ttl, offset ttl,0
invoke ExitProcess,0
end start
При компиляции в TASM пути к статическим библиотекам мы указывали при компиляции, в MASM пути к статическим библиотекам мы указываем в тексте программы точно так же как и пути к включаемым файлам с помощью директивы includelib.
Прототипы каждой из библиотек есть в одноимённых включаемых файлах в папке include. Теперь не надо писать прототипы функций самому, они уже есть:
includelib .\masm32\lib\kernel32.lib
includelib .\masm32\lib\user32.lib
include .\masm32\include\kernel32.inc
include .\masm32\include\user32.inc
В этих включаемых файлах определены функции без букв A или W в конце. Теперь не надо указывать эти буквы в конце.
Директива option нужна для задания некоторых настроек компиляции. Опция casemap задаёт чувствительность к регистру символов. Мы указали none, тем самым установили чувствительность к регистру символов. Это надо, для того чтобы избежать конфликтов включаемых файлов от разных авторов.
Компиляция.
Ассемблирование:
ML [ /опции ] filelist [ /link linkoptions ]
/c | ассемблирование без линковки В основном вы будете использовать эту опцию, так как вы будете использовать внешний линкер (например link. exe), для компоновки ваших файлов. |
/coff | генерировать объектный файл в COFF формате Это генерирует формат файла для компоновщика microsoft. |
/Fo<file> | имя объектного файла может использоваться, если вы хотите, чтобы выходной файл был с другим именем, не таким как исходный файл. |
/G<c|d|z> | Использует вызовы Pascal, C, или Stdcall выберите тип вызовов для ваших процедур. |
/Zi | Добавить символьную отладочную информацию Установите эту опцию, если хотите использовать отладчик. |
/I<name> | Установить include путь Определяет ваш include путь |
Линковка:
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 |


