Партнерка на США и Канаду по недвижимости, выплаты в крипто

  • 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 (циклический сдвиг вправо)

http://www.codenet.ru/progr/asm/newbee/lesson-11-1.gif

Как видно из рисунка выше, биты вращаются, то есть каждый бит, который выталкивается снова вставляется с другой стороны. Флаг переноса 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