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 в листинге программы имеют следующий вид: