Учебное пособие 903
.pdfЛюбая подпрограмма кроме задания последовательности действий может содержать описание некоторой совокупности локальных объектов – констант, типов, переменных и так далее. Эти объекты предназначены только для внутреннего использования в подпрограмме, внутри которой они объявлены.
Подпрограмма может быть предназначена для работы с различными данными. Гибкость и универсальность подпрограммного механизма обеспечивают с помощью понятия параметров, которые используются в объявление подпрограммы.
Часто в программах, особенно больших, приходится несколько раз решать одну и ту же подзадачу. Чтобы избежать повторного выписывания одной и той же й группы команд, ее обычно записывают один раз и оформляют соответствующим образом, а затем в нужных местах программы просто передают управление на эти команды, которые, проработав, возвращают управление обратно. В языке ассемблера есть несколько средств, решающих проблему дублирования фрагментов программного кода. К ним относятся:
-процедуры;
-макроподстановки (макроассемблер);
-генерация и обработка программных прерываний. Подпрограмма представляет собой совокупность
команд, операторов и выражений для решения конкретной подзадачи и обладает средствами получения управления из точки вызова задачи более высокого уровня и возврата управления в эту точку. Подпрограммы принято оформлять в виде процедур.
имя-процедурыPROC [far или |
near] |
<команды тела процедуры>
RET [n]; обязательная команда возврата
имя-процедурыENDP
9
Директива PROC указывает начало процедуры. Имя процедуры должно обязательно присутствовать. Параметр у директивы PROC может быть near(близкий) и far (дальний). К близкой процедуре можно обращаться только из того сегмента команд, где она описана, и нельзя обращаться из других сегментов, а к дальней процедуре можно обращаться из любых сегментов команд. Имена и метки, описанные в процедуре, не локализуются внутри нее. Конец описания процедуры указывает директива ENDP.
Обычно подпрограммы размещают либо в конце сегмента команд, либо в самом начале этого сегмента - перед той командой, с которой должно начинаться выполнение основной программы. Если имеется несколько подпрограмм, то их обычно размещают последовательно. В больших программах подпрограммы часто размещают в отдельном сегменте.
Подпрограмма может выполняться лишь тогда, когда ее вызовут (к ней обратятся).
Пример оформления модульной программы в одном сегменте кода.
A_codeSEGMENTpara
Assume cs:a_code, ss:a_stack, ds:a_data
; описание подпрограммы 1
P1 procnear
Команды алгоритма подпрограммы 1
Ret
P1 endp
; описание подпрограммы 2
P2 procnear
Команды алгоритма подпрограммы 2
Ret
10
P2 endp
; начало команд главной программы
Begin:mov |
ax, |
a_data |
mov ds, ax
Команды алгоритма главной программы
;Вызов подпрограммы 1
Call P1
Команды главной программы
;Вызов подпрограммы 2
Call P2
Команды главной программы
……..
Mov ah, 4ch
Int 21h
A_code ends
Endbegin
Выполнение главной программы начинается командой с меткой begin. Вызов процедур осуществляется с помощью команды управленияCall. При этом существуют несколько видов обращения к процедуре:
1.Явное указание имени процедуры: Call имя
процедуры.
Пример: Call P1.
2.С помощью косвенной адресации:
Call [регистр индекса или базы]
Call [Имя переменной из сегмента данных] Пример: Call [BX].
11
Механизм исполнения команды Call в общем виде состоит из двух этапов:
•Содержимое регистра IP помещается в стек, то есть, на вершине стека сохраняется адрес команды, следующей после команды Call - адрес возврата.
•В регистр IP заносится адрес вызываемой процедуры, и начинает выполняться процедура, выполнение главной программы приостанавливается в это время.
Команда RET осуществляет возврат из процедуры в вызывающий модуль. Содержимое вершины стека (адрес возврата) пересылается в регистр IP, таким образом, управление возвращается в вызывающий модуль. Если была передача параметров через стек, то команда возврата RET должна быть с параметром N. В данном случае возврат сопровождается «выталкиванием» из стека загруженных фактических параметров, занимавших N байтов в стеке. Если главная программа оформлена в виде процедуры, то в директиве END пишется имя главной процедуры: END имя
главной процедуры.
Пример:
A_CODE SEGMENT PARA
P_MAIN PROC FAR
ASSUME ……..
………
RET
P_MAIN ENDP
A_CODE ENDS
Endp_main
12
В |
начало |
главной |
процедуры |
добавляются |
три |
дополнительных команды
PUSH DS
;сохранение текущего адреса сегмента данных в стеке
XOR AX, AX
PUSH AX
; выполняется инициализация регистра DS
MOV AX, A_ DATA
MOV DS, AX
Процедуры могут быть с параметрами. В этом случае обмен данным между процедурами может быть выполнен либо через регистры общего назначения, либо через стек. Процедуры без параметров обычно получают или возвращают данные через области, доступные как вызывающему, так и вызываемому модулю: общий сегмент данных (глобальные переменные), определённая область оперативной памяти с заданным физическим адресом или через файлы.
Примеры программ с использованием процедур.
Пример 1.
Оформить группу команд для вывода сообщения в виде внутренней процедуры. Адрес начала сообщения передаётся в процедуру через регистр DX. Используется прерывание DOS 21h, функция вывода 9h. Текст сообщения должен завершаться знаком $.
WRITE_MPROCNEAR
;Процедура с одним параметром, вывод сообщения
;DX – адрес начала сообщения($)
13
MOVAh, 09h
Int 21h
RET
WRITE_ MENDP
В вызывающем модуле:
………………………………..
; Регистр DX надо инициализировать«вручную»перед CALL:
LEADX, MES1 ; адрес первого сообщения в DX
CALLWRITE_M ; явный вызов процедуры WRITE _ M
……..
LEADX, MES 2; адрес сообщения MES2 в DX
CALLWRITE_M ; явный вызов процедуры WRITE _ M
……………………….
Пример 2.
Вычислить y = K! + 5!, где n<=10.
Алгоритм вычисления факториала оформим процедурой ближнего типа с одним параметром, передаваемым через регистр AX. Результат возвращается в регистре BX.
A_datasegmentpara
K DW 7
Y DW ?
A_data ends
A_codeSEGMENT para
14
Assume cs:a_code, |
ss:a_stack, ds:a_data |
|
|
; описание подпрограммы 1 |
|
|
|
FAKTPRICNEARr |
;Заголовок процедуры |
||
; процедура вычисления N! |
|
|
|
; регистр AX - входной параметр, значение N. |
|
||
; регистр BX - возвращаемое значениеN!. |
|
||
; арифметика для чисел БЕЗ знака |
|
|
|
PushCX |
; сохранение в стеке регистра CX |
||
MOV CX, AX |
;инициализация счетчика |
||
MOV AX, 1 |
;инициализация произведения |
||
BODY: |
;метка начала цикла |
|
|
MUL CX |
;AX*CX ->AX; вычисляем факториал |
||
LOOPBODY |
; CX=CX-1; если |
CX<>0, |
переходим на |
BODY |
|
|
|
MOVBX, AX |
; результат ->BX |
|
|
PopCX; восстановление регистра CX |
|
|
|
RET |
; возврат: |
IP := |
содержимое |
вершины стека |
|
|
|
FAKT ENDP |
; конецпроцедуры |
|
|
Main proc far |
|
|
|
… |
|
|
|
MOV AX, N |
; AX = N |
|
|
15
CALL FAKT |
|
; Вызов процедуры FAKT, адрес возврата |
|
– в стек |
|
|
|
MOV Y, BX |
|
; Y = BX |
(N!) |
MOVAX, 5 |
|
; AX = 5 |
|
CALLFAKT |
|
; Вызов процедуры FAKT, адрес возврата |
|
– в стек |
|
|
|
ADDY, BX |
|
; Y = Y+5! |
|
MOVAX, Y |
|
; подготовка к выводу |
|
PRINT_NUMBER |
; макрокоманда вывода числа из регистра |
||
AX |
|
|
|
… |
|
|
|
MAINENDP |
|
|
|
A_code |
ends |
|
|
Endmain |
|
|
|
16
4. ПРИМЕР ПРОГРАММЫ КУРСОВОЙ РАБОТЫ
Заданы два массива X[N] и Y[M], где N<=10, M<=8 . Найти количество четных элементов каждого массива. Программа должна иметь модульную структуру. Алгоритмы ввода и вывода массивов выделить в отдельные процедуры. Вычисление количества четных элементов в массиве оформить
ввиде процедуры. Передача параметров через регистры:
вВХ – смещение массива;
вСХ – число элементов в массиве;
в АХ – результат вычислений (число четных элементов массива).
Блок-схема главного модуля изображена на рис. 2.
17
НАЧАЛО
ABOUT
ввод массива Хvvod_mas
vvod_mas
ввод массива Y
Bx=X CX=10
SCHET
KOLX=AX
Bx=Y CX=12
SCHET
KOLY=AX
Bx=Y CX=12 print_mas
Ax=KOLX
Bx=X CX=10 print_mas
AX=KOLY print
КОНЕЦ
Рис. 2. Блок-схема главного модуля
18