Call subprog
.
.
PUBLIC SUBPROG
SUBPROG .
.
.
RET
Команда CALL в MAINPROG должна "знать", что SUBPROG существует вне данного сегмента (иначе ассемблер выдаст сообщение о том, что идентификатор SUBPROG не определен). С помощью директивы EXTRN можно указать ассемблеру, что ссылка на SUBPROG имеет атрибут FAR, т.е. определена в другом ассемблерном модуле. Так как сам
ассемблер не имеет возможности точно определить такие ссылки, он генерирует "пустой" объектный код для последующего заполнения его при компоновке:
9А 0000 ---- Е
Подпрограмма SUBPROG содержит директиву PUBLIC, которая указывает ассемблеру и компоновщику, что другой модуль (MAINPROG) должен "знать" адрес SUBPROG.
Модули MAINPROG и SUBPROG ассемблируются отдельно, а компонуются совместно компоновщиком tlink.exe.
Компоновщик устанавливает соответствия между адресами EXTRN в одном объектном модуле с адресами PUBLIC в другом и заносит необходимые относительные адреса. Затем он объединяет два объектных модуля в один выполняемый. При невозможности разрешить ссылки компоновщик выдает сообщения об ошибках. Следите за этими сообщениями, прежде чем пытаться выполнить программу.
5. Директива EXTRN имеет следующий формат:
EXTRN имя:тип [,...]
Можно определить более одного имени (до конца строки) или закодировать дополнительные директивы EXTRN. В другом ассемблерном модуле соответствующее имя должно быть определено и идентифицировано как PUBLIC. Существуют следующие типы элементов: ABS, BYTE, DWORD, FAR, NEAR, WORD. Имя может быть определено через EQU и должно удовлетворять реальному определению имени.
6. Директива PUBLIC указывает ассемблеру и компоновщику, что адрес указанного идентификатора доступен из других программ. Директива имеет следующий формат:
PUBLIC идентификатор [,...]
Можно определить более одного идентификатора (до конца строки) или закодировать дополнительные директивы PUBLIC. Идентификаторы могут быть метками, переменными или числами. Неправильными идентификаторами являются имена регистров и EQU-идентификаторы, определяющие значения более двух байт.
ПОРЯДОК ВЫПОЛНЕНИЯ РАБОТЫ:
1. Программа: использование директив EXTRN и PUBLIC для меток.
1.1. Записать в текстовом редакторе следующую программу в ЕХЕ-формате:
.286
TITLE CALLMUL1 (EXE) Вызов подпрограммы умножения
;-------------------------------------------------------
EXTRN SUBMUL:FAR
;-------------------------------------------------------
STACKSG SEGMENT PARA STACK 'Stack'
DW 64 DUP(?)
STACKSG ENDS
;-------------------------------------------------------
DATASG SEGMENT PARA 'Data'
QTY DW 0140H
PRICE DW 2500H
DATASG ENDS
;--------------------------------------------------------
CODESG SEGMENT PARA 'Code'
BEGIN PROC FAR
ASSUME CS:CODESG,DS:DATASG,SS:STACKSG
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DATASG
MOV DS,AX
MOV AX,PRICE ;Загрузить стоимость
MOV BX,QTY ;и количество
CALL SUBMUL ;Вызвать подпрограмму
RET ;Вернуться в DOS
BEGIN ENDP
CODESG ENDS
END BEGIN
Записать эту программу на диск под именем callmul1.asm.
1.2. Записать в текстовом редакторе следующую подпрограмму:
.286
TITLE SUBMUL1 Подпрограмма для умножения
;-------------------------------------------------------
CODESG SEGMENT PARA 'Code'
SUBMUL PROC FAR
ASSUME CS:CODESG
PUBLIC SUBMUL
MUL BX ;AX-стоимость,
;ВХ-количество
;Произведение в DX:AX
RET ;Вернуться в DOS
SUBMUL ENDP
CODESG ENDS
END SUBMUL
Записать эту подпрограмму на диск под именем submul1.asm.
1.3. Выполнить ассемблирование основной программы и подпрограммы. Для этого в командной строке DOS необходимо ввести следующую команду:
tasm.exe callmul1.asm+submul1.asm /l
При этом ассемблер создаст два obj-модуля и два листинга для основной программы и подпрограммы соответственно.
1.4. Просмотреть листинги основной программы и подпрограммы и записать их в отчет.
В основной программе определены сегменты для стека, данных и кода. В сегменте данных определены поля QTY и PRICE. В кодовом сегменте регистр АХ загружается значением PRICE, а регистр ВХ - значением QTY, после чего происходит вызов подпрограммы. Директива EXTRN в основной программе определяет SUBMUL как точку входа в подпрограмму.
Подпрограмма содержит директиву PUBLIC (после ASSUME), которая указывает компоновщику, что точкой входа для выполнения является метка SUBMUL. Подпрограмма умножает содержимое регистра АХ (цена) на содержимое регистра ВХ (количество). Результат умножения вырабатывается в регистровой паре DX:AX в виде числа 002Е 4000.
Так как подпрограмма не определяет каких-либо данных, то ей не требуется сегмент данных. Если бы подпрограмма имела сегмент данных, то только она одна использовала бы свои данные.
Также в подпрограмме не определен стековый сегмент, так как она использует те же стековые адреса, что и основная программа. Таким образом, стек, определенный в основной программе, является доступным и в подпрограмме. Для компоновщика необходимо обнаружить по крайней мере один стек и определение стека в основной программе является достаточным.
1.5. Выполните компоновку основной программы и подпрограммы.
Для этого в командной строке DOS введите следующую команду:
tlink.exe callmul1.obj+submul1.obj
Компоновщик создаст загрузочный модуль callmul1.exe.
1.6. Выполните трассировку программы с помощью отладчика DEBUG. Обратите внимание как изменяется содержимое регистров CS и IP при вызове подпрограммы и при возврате в основную программу. Выводы о работе программы запишите в отчет.
2. Программа: использование директивы PUBLIC в кодовом сегменте.
2.1. Записать в текстовом редакторе следующую программу в ЕХЕ-формате:
.286
TITLE CALLMUL2 (EXE) Вызов подпрограммы умножения
;-------------------------------------------------------
EXTRN SUBMUL2:FAR
;-------------------------------------------------------
STACKSG SEGMENT PARA STACK 'Stack'
DW 64 DUP(?)
STACKSG ENDS
;-------------------------------------------------------
DATASG SEGMENT PARA 'Data'
QTY DW 0140H
PRICE DW 2500H
DATASG ENDS
;--------------------------------------------------------
CODESG SEGMENT PARA PUBLIC 'Code'
BEGIN PROC FAR
ASSUME CS:CODESG,DS:DATASG,SS:STACKSG
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DATASG
MOV DS,AX
MOV AX,PRICE ;Загрузить стоимость
MOV BX,QTY ;и количество
CALL SUBMUL2 ;Вызвать подпрограмму
RET ;Вернуться в DOS
BEGIN ENDP
CODESG ENDS
END BEGIN
Записать эту программу на диск под именем callmul2.asm.
2.2. Записать в текстовом редакторе следующую подпрограмму:
.286
TITLE SUBMUL2 Вызываемая подпрограмма умножения
;-------------------------------------------------------
CODESG SEGMENT PARA PUBLIC 'Code'
SUBMUL2 PROC FAR
ASSUME CS:CODESG
PUBLIC SUBMUL2
MUL BX ;AX-стоимость,
;ВХ-количество
;Произведение в DX:AX
RET ;Вернуться в DOS
SUBMUL2 ENDP
CODESG ENDS
END SUBMUL2
Записать эту подпрограмму на диск под именем submul2.asm.
2.3. Выполнить ассемблирование основной программы и подпрограммы.
Просмотреть листинги основной программы и подпрограммы и записать их в отчет.
Для данного примера имеется одно изменение в основной программе и одно - в подпрограмме. В обоих случаях в директиве SEGMENT используется атрибут PUBLIC.
Из таблицы идентификаторов (в конце каждого листинга ассемблирования) следует, что обобщенный тип кодового сегмента CODESG - PUBLIC (в предыдущем примере было NONE). При этом компоновщик объединит два логических кодовых сегмента в один физический кодовый сегмент.
Таким образом подпрограмма будет находиться в общем с основной программой кодовом сегменте.
2.4. Выполните компоновку основной программы и подпрограммы.
Выполните трассировку программы с помощью отладчика DEBUG. Обратите внимание как изменяется содержимое регистров CS и IP при вызове подпрограммы и при возврате в основную программу. Выводы о работе программы запишите в отчет.
3. Программа: общие данные в подпрограмме.
3.1. Записать в текстовом редакторе следующую программу в ЕХЕ-формате:
.286
TITLE CALLMUL3 (EXE) Вызов подпрограммы для умножения
;-------------------------------------------------------
EXTRN SUBMUL3:FAR
PUBLIC QTY,PRICE
;-------------------------------------------------------
STACKSG SEGMENT PARA STACK 'Stack'
DW 64 DUP(?)
STACKSG ENDS
;-------------------------------------------------------
DATASG SEGMENT PARA PUBLIC 'Data'
QTY DW 0140H
PRICE DW 2500H
DATASG ENDS
;--------------------------------------------------------
CODESG SEGMENT PARA PUBLIC 'Code'
BEGIN PROC FAR
ASSUME CS:CODESG,DS:DATASG,SS:STACKSG
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DATASG
MOV DS,AX
CALL SUBMUL3 ;Вызвать подпрограмму
RET ;Вернуться в DOS
BEGIN ENDP
CODESG ENDS
END BEGIN
Записать эту программу на диск под именем callmul3.asm.
3.2. Записать в текстовом редакторе следующую подпрограмму:
.286
TITLE SUBMUL3 Подпрограмма для умножения
;------------------------------------------------------
EXTRN QTY:WORD,PRICE:WORD
;-------------------------------------------------------
CODESG SEGMENT PARA PUBLIC 'Code'
SUBMUL3 PROC FAR
ASSUME CS:CODESG
PUBLIC SUBMUL3
MOV AX,PRICE
MOV BX,QTY
MUL BX ;Произведение в DX:AX
RET ;Вернуться в DOS
SUBMUL3 ENDP
CODESG ENDS
END SUBMUL3
Записать эту подпрограмму на диск под именем submul3.asm.
3.3. Выполнить ассемблирование основной программы и подпрограммы.
Просмотреть листинги основной программы и подпрограммы и записать их в отчет.
Для данного примера области QTY и PRICE по-прежнему определяются в основной программе, но загрузка значений из этих областей в регистры АХ и ВХ выполняется в подпрограмме.
В основной программе имена QTY и PRICE определены как PUBLIC. Сегмент данных также определен с атрибутом PUBLIC.
В подпрограмме имена QTY и PRICE определены как EXTRN и WORD. Такое определение указывает ассемблеру на длину этих полей в 2 байт. Благодаря этому ассемблер сгенерирует правильный код операции для команд MOV, а компоновщик установит значения операндов.
Команды MOV в листинге программы имеют следующий вид: