Например,
MOV DX, seg FLDW ; DX := адрес сегмента данных
TYPE – возвращает число байтов, соответствующее определению имени в декларациях:
Определение | Возвращаемое значение |
DB | 1 |
DW | 2 |
DD | 4 |
DQ | 8 |
DT | 10 |
STRUC | Число Байтов, определенных в STRUC |
NEAR {метка} | FFFFh |
FAR {метка} | FFFEh |
Например, для Tabl, описанной выше, можно записать
MOV AX, Type Tabl; AX := 0002h
SIZE – возвращает произведение длины LENGTH и типа TYPE (подсчитывает число байтов, потраченных на запись) и полезен при ссылках на переменную с оператором DUP.
Для использованного выше примера можно записать
MOV BX, Size Tabl ; BX := 0014h
Блочная структура программы. Процедуры.
Часто в больших программах используются подпрограммы для реализации вспомогательных алгоритмов. В ЯА подпрограммы оформляются в виде процедур.
Описание процедур
{имя проц.} PROC {параметр}
{тело проц.}
[RET]
{имя проц.} ENDP
где {имя проц.} – должно повторяться дважды и используется для обращения к процедуре;
{параметр} может принимать одно из двух значений - <NEAR> (по умолчанию) или <FAR>.
К близкой (внутренней) процедуре можно обращаться только из того сегмента команд, где она описана. К дальней (внешней) процедуре можно обращаться из любых сегментов команд программы, в том числе и из того, где она описана.
Имена и метки, описанные в процедуре, не локализуются внутри нее, поэтому должны быть уникальными в программе.
Хотя в АЯ можно описать одну процедуру внутри другой, никакой выгоды это не дает и обычно не используется.
Вызов процедур
На ЯА все переходы между основной программой и процедурой нужно организовывать самим. Если из процедуры возможен возврат в DOS, то ее можно вызвать командой перехода на имя процедуры
JMP {имя проц.}
Если нужен возврат в вызывающую программу, то проще всего использовать команду обращения
CALL {имя проц.}
Тогда в теле процедуры должна быть команда возврата
RET
Есть другая возможность: запомнить адрес возврата с использованием стека и организовать возврат командами переходов.
При вызове процедуры следует учитывать параметры, передаваемые процедуре, и ее расположение относительно точки вызова, т. е. тип перехода в команде CALL определяется автоматически, например (для процедуры p).
P
CALL P
Если это – близкий вызов (NEAR), то производятся следующие действия:
Stack := AB, IP := offset P
где АВ – адрес возврата, т. е. эффективный адрес команды, следующей за вызовом;
Дальний вызов (FAR) обеспечивает действия:
Stack := CS, Stack := AB, CS := seg P, IP := offset P
Если описание процедуры находится в сегменте ниже команды вызова, то следует указать атрибут перехода оператором PTR. Например,
CALL FAR PTR P; дальний вызов P
Расположение процедур в сегменте
1. Внутренние процедуры находятся в одном сегменте с вызывающей программой.
При этом возможны 3 варианта расположения:
а) Все процедуры размещены раньше основной (вызывающей) программы, которая может быть также оформлена в виде процедуры.
Например:
Text SEGMENT ‘code’
ASSUME CS: text, DS: data, SS: stack
A1 PROC
…
RET
A1 ENDP
Main PROC
MOV AX, data
MOV DS, AX
…
CALL A1
…
MOV AX, C400h
INT 21h
Main ENDP
Text ENDS
Data SEGMENT
…
Data ENDS
Stack SEGMENT ‘stack’
…
Stack ENDS
END Main
б) все процедуры – ниже точки вызова.
в) процедуры – внутри основной процедуры, возможно даже, что процедура внутри другой процедуры (хотя никакой выгоды это не даёт)
Например,
…
Main PROC
…
CALL A1
…
MOV AX, C400h
INT 21h
A1 PROC
…
RET
A1 ENDP
Main ENDP
Text ENDS
2) Внешние процедуры – располагаются в других сегментах или в других файлах.
Например, текст основной программы находится в файле P. asm
Text SEGMENT public ‘code’
; объединение модулей последовательно
; в общий сегмент
ASSUME CS: text, DS: data, SS: stack
EXTRN stop: proc; объявление внешнего имени
Main PROC
…
CALL Stop
…
Main ENDP
Text ENDS
Data SEGMENT
…
Data ENDS
Stack SEGMENT ‘stack’
…
Stack ENDS
END Main
Исходный текст процедуры находится в файле P1.asm
Text SEGMENT public ‘code’
ASSUME CS: text
PUBLIC stop; объявление имени доступным извне
Stop proc
…
ret
stop ENDP
text ENDS
END ; конец файла без точки входа
Объединение этих файлов происходит на шаге компоновки, т. е. требуется раздельная трансляция. Например, для MASM
MASM/ZI PR
MASM/ZI P1
где ZI – опция, позволяющая поместить в объектный файл полную информацию о номерах строк и символах исходного модуля (ИМ).
После образования PR. obj и P1.obj их нужно скомпоновать в единый загрузочный файл
LINK/C0 PR P1, COMPOZ
где С0 – опция, передающая в загрузочный файл символьную информацию, позволяющую отладчику CV выводить на экран полный текст ИМ, включая метки и комментарии.
Модуль COMPOZ. exe готов к исполнению.
Можно подключить процедуру из библиотеки. Для этого перед сегментами ИМ помещается директива
INCLUDE {имя файла библиотеки}
Например, для подключения файла IO. asm следует записать
INCLUDE IO. asm
S SEGMENT ‘stack’
…
S ENDS
D SEGMENT ‘data’
…
D ENDS
C SEGMENT ‘code’
ASSUME CS:C, SS:S, DS:D
Begin: …
…
C ENDS
END Begin
Передача параметров между процедурами (организуется по желанию программиста)
1. Передача параметров через регистры МП
Передавать значения фактических параметров можно через регистры МП по желанию программиста. Например,
; процедура вычисления AX := max {AX, BX}
max proc far
CMP AX, BX
JGE Max1
MOV AX, BX
Max1: RET
max endp
…
; в основной процедуре
…
MOV AX, A ; подготовка параметров
MOV BX, B ; к вызову процедуры
CALL max
MOV C, AX ; сохранение результата
…
2. Передача параметров по ссылке означает передачу адреса (имени) ячейки памяти, соответствующей фактическому параметру (передача именованного значения из ассемблера в Pascal). Для этого можно использовать имя ячейки памяти или загрузить адрес перед вызовом процедуры в регистр (BX, BP, SI или DI, т. к. в процедуре можно использовать эти регистры для адресации ).
Например, командой
LEA BX, B
CALL……
3. Передача параметров через стек.
Передача параметров через регистры ограничена их небольшим количеством. Если параметров много (больше 5-ти), их передают через стек следующим образом:
- Основная программа записывает в стек фактические параметры (значения или адреса);
- В процедуре используются параметры, записанные в стек.
Например:
; вызов p(a1,…, ak)
PUSH a1
…
PUSH ak
CALL p
…
В процедуре можно использовать дополнительный указатель стека BP, но в начале процедуры следует сохранить значение BP, которое использовалось в вызывающей программе, т. е.
; начало процедуры Р
P proc
PUSH BP ; сохранение BP
MOV BP, SP ; настройка BP на вершину стека
…
Затем можно использовать базовую адресацию. Например, для близкого вызова
[BP + 2] – адрес возврата, занесенный в стек автоматически,
[BP + 4] – адрес последнего параметра ak.
До возврата из процедуры следует восстановить BP командой
POP BP
затем очистить стек от передаваемых параметров, чтобы он не перегружался при многократном вызове процедур, т. е. установить в SP значение, на 2*k больше, чем было после вызова процедуры.
Есть 2 возможности корректного возврата из процедуры.
а) корректировать SP в вызывающей программе
; конец процедуры | ; в вызывающей программе |
POP BP | CALL p |
RET | ADD SP, 2*k ; коррекция SP |
P ENDP | … |
б) использовать команду возврата с восстановлением стека, имеющую вид для близкого вызова
RET {cnt}
где {cnt} – счетчик (константное выражение ), размером слово.
Команда выполняет следующие действия:
IP := Stack SP := SP + {cnt}
Тогда конец процедуры имеет вид:
POP BP
RET 2*k
p ENDP
Для дальнего вызова процедуры команда возврата имеет вид
RET {cnt}
и выполняет следующие действия:
IP := Stack CS := Stack SP := SP + {cnt}
При таком возврате из процедуры в вызывающей программе дополнительные действия не требуются.
4. Проблема сохранения регистров при обращении к процедуре
Чтобы процедура не портила значения регистров, которые использовались в вызывающей программе, требуется в тексте процедуры перед использованием какого-либо регистра сохранить его «старое» значение в стеке, а в конце процедуры все сохраненные значения восстановить.
Например, если в процедуре будет использоваться регистр CH, то его следует сохранить в стеке, но стек запоминает только со слова, поэтому в процедуре будет фрагмент:
PUSH CX ; сохранение «старого» CX
MOV CX, 0 ; использование CX в процедуре
…
; перед выходом из процедуры
POP CX ; восстановление «старого» CX
Таким образом, получится обобщенная схема близкой (NEAR) процедуры с параметрами, передаваемыми через регистры и через стек
{имя проц} proc

|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 |


