Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Тема 2.5 Функции языка ассемблер




Тема 2. 5 Функции языка ассемблер

MS-DOS и TASM 2. 0. Часть 14.

 

Параметры функции.

Функции в ассемблере — это часть кода, которая решает конкретную задачу или несколько, объединённых одной целью задач. Функция может вызываться без дополнительного дублирования кода. Человек способен помнить, воспринимать и использовать ограниченное число информации. Для облегчения понимания и создания кода его структурируют — дробят на определенные части. Функция — один из вариантов дробления кода — первый шаг к абстракции программы, упрощающий её структуру. Функция включает параметры.

Параметры функции:

Параметры ввода (Input Parameters или просто In ) — может быть сколько угодно.

Параметры вывода (Output Parameters или просто Out ) — может быть сколько угодно.

Возвращаемое значение (Return value или просто Return ) — только одно.

Таким образом, «стандартная» функция «MyFunc» имеет вид:

return MyFunc (In, In, Out, Out, In, Out, …);

Указанная (Си-подобная) структура функции условна. Любой из параметров может отсутствовать. Функция вообще может не иметь параметров.

Реализация вызовов функции в ассемблере.

Кроме параметров имеет значение алгоритм работы функции, так называемое «соглашение о вызове функции». Оно связано с построением работы в различных операционных системах различных высокоуровневых языков программирования.

В настоящее время существуют целый ряд принятых соглашений (конвенций) о вызове функций (подпрограмм): cdecl (язык Си, С++), pascal (язык Pascal), stdcall (WinApi), fastcall, safecall, thiscall. Все они начинали использоваться в разное время и с разными целями, решая определённые задачи, наиболее приемлемым способом.

Необходимо помнить, что для функции в ассемблере нет определённых договорённостей — вызывать можно так, как удобнее и оптимальнее. Мы рассмотрим наиболее используемые конвенции высокоуровневых языков программирования и их реализации на ассемблере.

Изучить примеры работы компиляторов языков высокого уровня крайне полезно, если вас интересует кодокопание в любом виде (крэкинг, реверс-программирование и др. ).

Передача параметров через регистры.

Самым очевидным, быстрым и простым способом передачи параметров функции в ассемблере может показаться способ передачи параметров через регистры:

Функция (процедура) содержит два параметра:
myFunc (a, b)

 

; Ассемблерный код: mov ax, a; Первый параметр (самый левый) - сверху mov bx, b; Второй параметр call myFunc   myFunc: ... (команды, которые могут использовать стек): add ax, 1; используем первый параметр ; Его адрес в сегменте стека ВР + 4, потому что при выполнении ; команды CALL при вызове функции, в стек поместили адрес возврата - 2 байта для процедуры ; типа NEAR (или 4 - для FAR), а потом еще и ВР - 2 байта (push bp - в начале нашей функции) mov bx, ax; используем второй параметр (ещё команды)   ret; Возвращаемся к коду вызова функции (call) из основного кода

Такой код имеет несколько недостатков, основной из которых — ограничение количества параметров (не больше, чем регистров). Есть и другие, менее очевидные сложности, которые мы не будем обсуждать в рамках этой статьи.

Из плюсов передачи параметров (и возврата значений) функции в ассемблере через регистры можно назвать скорость работы кода.

Теперь перейдём к рассмотрению более совершенных методов организации работы функций, которые используются языками высокого уровня.

Конвенция Передача параметров Освобождение стека
CDECL загоняются в стек слева направо — снизу вверх вызывающая программа
PASCAL загоняются в стек слева направо — сверху вниз сама процедура
STDCAL загоняются в стек слева направо — снизу вверх сама процедура

Конвенция вызова функций PASCAL (Pascal, Basic, Fortran и др. ).

Параметры загоняются в стек слева направо — сверху вниз, стек очищается вызываемой функцией:

Функция (процедура) содержит пять параметров:
myFunc (a, b, c, d, e)

 

; Ассемблерный код: push a; Первый параметр (самый левый) - сверху push b; Второй параметр push c; push d; push e; Пятый параметр - снизу call myFunc   ...   ; Функция содержит пять параметров: myFunc: push bp mov bp, sp; Создаём стековый кадр. В bp - указатель на стековый кадр, регистр bp использовать нельзя! a equ [bp+12]; Первый параметр - сверху ([bp+12]) b equ [bp+10] c equ [bp+8] d equ [bp+6] e equ [bp+4]; Пятый параметр ... ; команды, которые могут использовать стек: mov ax, e; Cчитать параметр 5 - [bp+4]. Можно и так, но это менее понятно: mov ax, [bp+4] ; Его адрес в сегменте стека ВР + 4, потому что при выполнении ; команды CALL при вызове функции, в стек поместили адрес возврата - 2 байта для процедуры ; типа NEAR (или 4 - для FAR), а потом еще и ВР - 2 байта (push bp - в начале нашей функции) mov bx, с; считать параметр 3 - [bp+8]. Можно и так, но это менее понятно: mov bx, [bp+8] ; ... ещё команды ... pop bp ret 10; Из стека дополнительно извлекается 10 байт - стек освобождает вызываемая функция

Конвенция вызова функций CDECL (Си, С++ и др. ).

Параметры загоняются в стек слева направо — снизу вверх, стек очищается вызывающая функция:

Функция (процедура) содержит пять параметров:
myFunc (a, b, c, d, e)

 

; Ассемблерный код: push e; Пятый параметр - сверху push d; push c; push b; Второй параметр push a; Первый параметр (самый левый) - снизу call myFunc add sp, 10; Стек освобождает вызывающая функция   ...   ; Функция содержит пять параметров: myFunc: push bp mov bp, sp; Создаём стековый кадр. В bp - указатель на стековый кадр, регистр bp использовать нельзя! e equ [bp+12]; Последний параметр - сверху ([bp+12]) d equ [bp+10] c equ [bp+8] b equ [bp+6] a equ [bp+4] ... ; команды, которые могут использовать стек: mov ax, e; считать пятый параметр - [bp+12]. Можно и так, но это менее понятно: mov ax, [bp+12] ; Его адрес в сегменте стека ВР + 4, потому что при выполнении ; команды CALL при вызове функции, в стек поместили адрес возврата - 2 байта для процедуры ; типа NEAR (или 4 - для FAR), а потом еще и ВР - 2 байта (push bp - в начале нашей функции) mov bx, с; считать третий параметр - [bp+8]. Можно и так, но это менее понятно: mov bx, [bp+8] ;... ещё команды ... pop bp ret; Из стека дополнительные байты не извлекаются - стек освободит код вызывающей программы (add sp, 10)

Конвенция вызова функций STDCALL (WinApi: Windows 95 — Windows 10).

Операционная система Windows содержит набор встроенных функций, которые обеспечивают удобство программирования, обслуживания и работы системы — так называемые WinApi — Windows Application programming interfaces.

Функций огромное количество. Они входят в стандартный пакет Windows любой версии и содержаться в библиотеках *. dll, расположенных в системных директориях Windows (System, System32, SysWOW64 — для 64 битной системы). Например, kernel32. dll содержит огромное количество функций, входящих в т. н. «Ядро операционной системы», например MoveFile — «переместить файл».

Для операционной системы Windows (WinApi) был разработана отдельная конвенция вызова функций. Она включила в себя преимущества PASCAL и С (Си) конвенций.

Конвенция STDCALL (WinApi) имеет следующие особенности.

Параметры загоняются в стек слева направо — снизу вверх, стек очищается вызываемая функция.

К слову можно сказать, что возвращаемое функцией WinApi значение содержиться в 32 битном регистре eax (нам он пока не известен, но это расширенный до 32 бит регистр ax).

Функция (процедура) содержит пять параметров:
myFunc (a, b, c, d, e)

 

; Ассемблерный код: push e; Пятый параметр - сверху push d; push c; push b; Второй параметр push a; Первый параметр (самый левый) - снизу call myFunc ... ; ---------------------------------------------------------------------- ; Функция содержит пять параметров: myFunc: push bp mov bp, sp; Создаём стековый кадр. В bp - указатель на стековый кадр, регистр bp использовать нельзя! e equ [bp+12]; Последний параметр - сверху ([bp+12]) d equ [bp+10] c equ [bp+8] b equ [bp+6] a equ [bp+4] ... ; команды, которые могут использовать стек: mov ax, e; считать параметр " a" - [bp+4]. Можно и так, но это менее понятно: mov ax, [bp+4] ; Команды CALL при вызове функции, в стек поместили адрес возврата - 2 байта для процедуры типа NEAR (или 4 - для FAR), а потом еще и ВР - 2 байта (push bp - в начале нашей функции) mov bx, с; считать параметр " c" - [bp+8]. Можно и так, но это менее понятно: mov bx, [bp+8] ;... ещё команды   ...   pop bp ret 10; Из стека дополнительно извлекается 10 байт - стек освобождает вызываемая функция

Преимущество C (Си) конвенции по сравнению с PASCAL.

1. Освобождение стека от параметров возлагается на вызывающую процедуру. Это позволяет отимизировать код.

Например, если вызываются несколькл функций подряд, принимающих одни и теже параметры, можно не заполнять стек заново:

 

.... push b; Второй параметр push a; Первый параметр - снизу call myFunc1 call myFunc2 add sp, 4; Стек освобождает вызывающая функция ....

2. Более просто создавать функции с изменяемым числом параметров (printf).

Преимущество PASCAL конвенции по сравнению с C (Си).

1. Освобождение стека производится внутри кода функции, что упрощает создание исходного кода, несколько уменьшает размер кода при многократном вызове функций.

Реализация конвенции PASCAL встроена в TASM с помощью макро-возможностей. Практическая реализация и код рассмотрены в статье «MS-DOS и TASM 2. 0. Часть 18. Упрощаем вызов функции в TASM«.

 

 

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...