- •Лекция №12. Процедуры в программах на ассемблере. Crc-код
- •1) Если имя – это имя переменной, то тип может принимать значения byte, word, dword, pword, fword, qword и tbyte;
- •3) Если имя – это имя константы, то тип должен быть abs.
- •Выполнить трансляцию модуля modul1.Asm и получить объектный модуль modul1.Obj;
- •Выполнить трансляцию модуля modul2.Asm и получить объектный модуль modul2.Obj;
- •Cкомпановать программу утилитой tlink командной строкой вида:
- •1) Используя последовательность из n команд рор хх.
- •2) Откорректировать регистр указателя стека sp на величину 2*n, например, командой
- •1) Сложение по правилам полиномиальной арифметики по модулю 2 (crc-арифметика), будет выполнено так (нет переносов):
- •2) Операция вычитания:
- •1) Согласование имен,
- •2) Согласование параметров,
- •3) Согласование вызовов.
- •Примеры
- •1) Пример использования директивы asm и команд сопроцессора в программе на языке Паскаль (Delphi 5.0).
- •3) Программа, которая использует функцию stdcall get_str_length с асемблерной вставкой, для нахождения длины asciiz-строки.
- •4) Программа, которая складывает два числа в десятичной системе исчисления и выдаёт результат тоже в десятичной системе исчисления. (Example2)
- •Выполнить трансляцию модуля modul1.Asm и получить объектный модуль modul1.Obj;
- •Выполнить трансляцию модуля modul2.Asm и получить объектный модуль modul2.Obj;
- •Cкомпановать программу утилитой tlink командной строкой вида:
- •4) В ассемблерном модуле вызываемая процедура должна быть дополнительно объявлена при помощи директивы public.
- •К имени процедуры в объектном модуле автоматически добавляется @8 (а не @0).
- •Ассемблер автоматически управляется со стеком.
- •3) Сложение двух целых чисел.
3) Если имя – это имя константы, то тип должен быть abs.
Пример использования директив extrn и public на схеме связи двух модулей Модуль1 и Модуль2.
; -------------------------- Модуль 1 ------------------------
masm
model small
.stack 100h
.data
… …
.code
My_proc_1 proc
…
My_proc_1 endp
My_proc_2 proc
…
My_proc_2 endp
; -----Объявляем процедуру My_proc_1 видимой извне ----------
public My_proc_1
start:
….
….
end start
; -------------------------- Модуль 2 ------------------------
masm
model small
.stack 100h
.data
… …
.code
Extrn my_proc_1 ; объявляем процедуру my_proc_1 внешней
start:
….
….
call my_proc_1 ; вызов my_proc_1 из модуля 1
end star
Если необходимо собрать два модуля, например modul1.asm и modul2.asm в один исполняемый модуль, необходимо:
-
Выполнить трансляцию модуля modul1.Asm и получить объектный модуль modul1.Obj;
-
Выполнить трансляцию модуля modul2.Asm и получить объектный модуль modul2.Obj;
-
Cкомпановать программу утилитой tlink командной строкой вида:
tlink /v modul1.obj+ modul2.obj
В итоге будет создан исполняемый модуль modul1.exe и можно будет исследовать данный модуль в отладчике, но при этом в турбо дебагере можно будет увидеть только текст программы modul1. Для того, чтобы войти в вызываемую процедуру, необходимо нажимать клавишу F7. Обработка этой команды приведёт к открытию второго окна, в котором будет выведен текст вызванной процедуры.
Передача параметров
Процедуры могут получать или не получать параметры из вызывающей процедуры и могут возвращать или не возвращать результаты (процедуры, которые что-либо возвращают, называются функциями, но ассемблер не делает каких-либо различий между ними).
Параметры можно передавать с помощью одного из следующих механизмов:
– по значению;
– по ссылке;
….
Параметры можно передавать в одном из следующих мест:
– в регистрах;
– в глобальных переменных;
– в стеке;
Передача параметров по значению
Процедуре передается собственно значение параметра. При этом фактически значение параметра копируется, и процедура использует его копию, так что модификация исходного параметра оказывается невозможной. Этот механизм применяется для передачи небольших параметров, таких как байты или слова.
Например, если параметры передаются в регистрах:
mov ax,word ptr value ; сделать копию значения
call procedure ; вызвать процедуру
Передача параметров по ссылке
Процедуре передается не значение переменной, а ее адрес, по которому процедура должна сама прочитать значение параметра. Этот механизм удобен для передачи больших массивов данных и для тех случаев, когда процедура должна модифицировать параметры, хотя он и медленнее из-за того, что процедура будет выполнять дополнительные действия для получения значений параметров.
mov ax,offset value
call procedure
Передача параметров в регистрах
Если процедура получает небольшое число параметров, то лучше всего их передавать через регистры. Примерами использования этого метода могут служить практически все вызовы прерываний DOS и BIOS. Языки высокого уровня обычно используют регистр АХ (ЕАХ) для того, чтобы возвращать результат работы функции.
Недостатки данного способа:
– существуют ограничения на способ передачи аргументов через регистры;
– имеется небольшое число доступных для пользователя регистров;
– необходимо постоянно помнить о том, какая информация в каком регистре находится;
– имеется ограничение размера передаваемых данных размерами регистра. Если размер данных превышает 16 или 32 бита, то передачу данных посредством регистров произвести нельзя. В этом случае передавать нужно не сами данные, а указатели на них.
Передача параметров в глобальных переменных
Когда не хватает регистров, один из способов обойти это ограничение — записать параметр в переменную, к которой затем обращаться из процедуры. Этот метод неэффективный, так как делает невозможным рекурсию и повторную входимость в процедуру.
Передача параметров в стеке
Для чтения параметров процедуры из стека используют не команду POP, а регистр ВР, в который помещают адрес вершины стека после входа в процедуру.
Текущее значение вершины стека хранится в регистре ESP. При записи в стек значение этого регистра уменьшается, то есть стек растет вниз от максимально возможного адреса (рис. 4).
Рис. 4. Стек.
При вызове подпрограммы параметры в большинстве случаев помещают в стек, а в EBP записывают текущее значение ESP. Тогда, если подпрограмма использует стек для хранения локальных переменных, ESP изменится, но EBP можно будет использовать для того, чтобы считывать значения параметров напрямую из стека (их смещения будут записываться как EBP+номер параметра).
Перед использованием стека для доступа к данным его содержимое необходимо правильно инициализировать, что предполагает формирование в нём адреса, который бы указывал на непосредственно переданные данные. Для этого в начало процедуры необходимо включить дополнительный фрагмент кода, который называется - пролог.
Параметры в стеке, адрес возврата и старое значение ВР вместе называются активационной записью функции.
Для удобства ссылок на параметры, переданные в стеке, внутри функции иногда используют директивы EQU, чтобы не писать каждый раз точное смещение параметра от начала активационной записи (то есть от ВР), например так:
push X
push Y
push Z
call xyzzy
[...] ; x = 2 байта FF
xyzzy proc near ; y = 2 байта FD
; z = 2 байта FB
; ip =2 байта F9 < IP
; SP=F7 BP<SP; BP=F7
Пролог
mov bp,sp ;
;
xyzzy_x equ [bp+8]
xyzzy_y equ [bp+6]
xyzzy_z equ [bp+4]
(команды, которые могут использовать стек)
mov ax,xyzzy_x ;считать параметр X
(остальные команды)
Эпилог
pop bp
ret 6
xyzzy endp
Удалять параметры из стека можно:
-
в процедуре;
-
в вызывающей процедуру программе;
Если за освобождение стека от параметров отвечает вызывающая программа, то становится возможным вызвать несколько процедур с одними и теми же параметрами просто последовательными командами CALL.
Фрагмент кода, обеспечивающий корректный возврат из процедуры называется эпилог процедуры (корректировка содержимого стека, удаление из него аргументов, которые стали не нужны и которые передавались в процедуру).
Очистку стека от ненужных аргументов можно выполнить несколькими способами: