- •5. Препроцессор. Директивы препроцессора.
- •7.Работа с файлами. Текстовый и двоичный режим.
- •10.Перечислимый тип. Структуры. Объединения.
- •13.Спецификаторы класса памяти.
- •14.Пространства имён.
- •16.Понятие класса.
- •17.Функции-члены класса. Указатель this.
- •18.Конструкторы. Деструкторы.
- •19.Преобразования объектов класса.
- •20.Доступ к членам класса.
- •21.Статические члены класса.
- •23.Совместное использование.
- •27.Производные классы.
- •29.Указатели на члены класса.
- •30.Множественное наследование.
- •31.Структура dll-библиотеки.
- •32.Статическое и динамическое подключение dll-библиотек.
- •34.Регистры процессора.
- •36.Арифметические команды в языке ассемблера.
- •37.Команды сравнения и перехода в языке ассемблера.
- •38.Команды работы с битами в языке ассемблера.
- •39.Процедуры в языке ассемблера. Передача параметров в процедуру.
- •40.Процедуры в языке ассемблера. Возврат результата. Локальные данные.
39.Процедуры в языке ассемблера. Передача параметров в процедуру.
Синтаксис процедуры
Описание процедуры на языке ассемблера выглядит следующим образом:
<имя процедуры> PROC
<тело процедуры>
<имя процедуры> ENDP
Несмотря на то, что после имени процедуры не ставится двоеточие, это имя является меткой, обозначающей первую команду процедуры.
В языке ассемблера имена и метки, описанные в процедуре, не локализуются внутри неё, поэтому они должны быть уникальны.
Размещать процедуру в программе на языке ассемблера следует таким образом, чтобы команды процедуры выполнялись не сами по себе, а только тогда, когда происходит обращение к процедуре. Обычно процедуры размещают либо в конце секции кода после вызова функции ExitProcess, либо в самом начале секции кода, сразу после директивы .code.
Передача параметров процедуры
Существуют несколько способов передачи параметров в процедуру.
Параметры можно передавать через регистры.
Если процедура получает небольшое число параметров, идеальным местом для их передачи оказываются регистры. Существуют соглашения о вызовах, предполагающие передачу параметров через регистры ECX и EDX. Этот метод самый быстрый, но он удобен только для процедур с небольшим количеством параметров.
Параметры можно передавать в глобальных переменных.
Параметры процедуры можно записать в глобальные переменные, к которым затем будет обращаться процедура. Однако этот метод является неэффективным, и его использование может привести к тому, что рекурсия и повторная входимость3 станут невозможными.
Параметры можно передавать в блоке параметров.
Блок параметров – это участок памяти, содержащий параметры и располагающийся обычно в сегменте данных. Процедура получает адрес начала этого блока при помощи любого метода передачи параметров (в регистре, в переменной, в стеке, в коде или даже в другом блоке параметров).
Параметры можно передавать через стек.
Передача параметров через стек – наиболее распространённых способ. Именно его используют языки высокого уровня, такие как С++ и Паскаль. Параметры помещаются в стек непосредственно перед вызовом процедуры.
Параметры можно передавать в потоке кода.
В этом необычном методе передаваемые процедуре данные размещаются прямо в коде программы, сразу после команды CALL. Чтобы прочитать параметр, процедура должна использовать его адрес, который автоматически передаётся в стеке как адрес возврата из процедуры. Разумеется, процедура должна будет изменить адрес возврата на первый байт после конца переданных параметров перед выполнением командыRET.
.686
.model flat, stdcall
option casemap: none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.code
program:
call Procedure ; Команда CALL кладёт в стек адрес следующей команды
db 'string',0 ; В нашем случае – адрес начала строки
push 0
call ExitProcess
Procedure proc
pop esi ; Извлекаем из стека адрес начала строки
xor eax, eax ; Обнуляем EAX, в нём будет храниться количество символов
L1: mov bl, [esi] ; Заносим в регистр BL байт, хранящийся по адресу ESI
inc esi ; Увеличиваем значение в регистре ESI на 1
inc eax ; Увеличиваем значение в регистре EAX на 1
cmp bl, 0 ; Сравниваем прочитанный символ с нулём
jne L1 ; Если не 0, переходим к началу цикла
push esi ; Кладём в стек адрес байта, следующего сразу за строкой
ret ; Возврат из процедуры
Procedure endp
end program
