0003 SUBMUL ENDP
0003 CODESG ENDS
END SUBMUL
Segments and Groups:
N a m e Size Align Combine Class
CODESG. . . . . . . . . . . . . 0003 PARA PUBLIC 'CODE'
Symbols:
N a m e Type Value Attr
SUBMUL. . . . . . . . . . . . .F PROC 0000 CODESG Global Length=0003
LINK
IBM Personal Computer Linker
Version 2.30 (C) Copyright IBM Corp 1981, 1985
Object Modules: B:CALLMUL2+B:SUBMUL2
Run File: [B:CALLMUL2.EXE]:
List File: [NUL.MAP]: CON
Libraries [.LIB]:
Start Stop Length Name Class
00000H 00022H 0023H CODESG CODE <-- Примечание: 1 сегмент кода
00030H 00033H 0004H DATASG DATA
00040H 000BFH 0080H STACKSG STACK
Program entry point at 0000:0000
__________________________________________________________________________
Рис.21.4. Кодовый сегмент, определенный как PUBLIC.
Следующий пример на рис.21.4 представляет собой вариант программы на
рис.21.3. Имеется одно изменение в основной программе и одно - в
подпрограмме. В обоих случаях в директиве SEGMENT используется атрибут
PUBLIC:
CODESG SEGMENT PARA PUBLIC 'CODE'
Рассмотрим результирующую карту компоновки и объектный код команды
CALL.
Из таблицы идентификаторов (в конце каждого листинга ассемблирования)
следует: обобщенный тип кодового сегмента CODESG - PUBLIC (на рис.21.3
было NONE). Но более интересным является то, что карта компановки в конце
листинга показывает теперь только один кодовый сегмент! Тот факт, что оба
сегмента имеют одни и те же имя (CODESG), класс ('CODE') и атрибут PUBLIC,
заставил компоновщика объединить два логических кодовых сегмента в один
физический кодовый сегмент. Кроме того, при трассировке выполнения
программы можно обнаружить, что теперь команда вызова подпрограммы имеет
следующий объектный код:
9A 2000 D213
Эта команда заносит шест.2000 в регистр IP и шест.D213 в регистр CS. Так
как подпрограмма находится в общем с основной программой кодовом сегменте,
то в регистре CS устанавливается тот же стартовый адрес - шест.D213. Но
теперь смещение равно шест.0020:
Адрес в CS: 13D20
Смещение в IP: 0020
Действительный адрес: 13D40
Таким образом, кодовый сегмент подпрограммы начинается, очевидно, по
адресу шест.13D40. Правильно ли это? Карта компановки не дает ответа на
этот вопрос, но можно определить адрес по листингу основной программы,
которая заканчивается на смещении шест.0016. Так как кодовый сегмент для
подпрограммы определен как SEGMENT, то он должен начинаться на границе
параграфа, т.е. его адрес должен нацело делиться на шест.10 или правая
цифра адреса должна быть равна 0. Компоновщик размещает подпрограмму на
ближайшей границе параграфа непосредственно после основной программы -
этот относительный адрес равен шест.00020. Поэтому кодовый сегмент
подпрограммы начинается по адресу 13D20 плюс 0020 или 13D40.
__________________________________________
| Основная программа... | Подпрограмма |
| (не используемый участок) | |
|___________________________|______________|
| | |
13D20 13D30 13D40
Рассмотрим, каким образом компоновщик согласует данные, определенные в
основной программе и имеющие ссылки из подпрограммы.
ПРОГРАММА: ОБЩИЕ ДАННЫЕ В ПОДПРОГРАММЕ
________________________________________________________________
__________________________________________________________________________
page 60,132
TITLE CALLMUL3 (EXE) Вызов подпрограммы
; для умножения
EXTRN SUBMUL:FAR
PUBLIC QTY,PRICE
;-------------------------------------------------
0000 STACKSG SEGMENT PARA STACK 'Stack'
0000 40 [????] DW 64 DUP(?)
0080 STACKSD ENDS
;-------------------------------------------------
0000 DATASG SEGMENT PARA PUBLIC 'Data'
0000 0140 QTY DW 0140H
0002 2500 PRICE DW 2500H
0004 DATASG ENDS
;-------------------------------------------------
0000 CODESG SEGMENT PARA PUBLIC 'Code'
0000 BEGIN PROC FAR
ASSUME CS:CODESG,DS:DATASG,SS:STACKSG
0000 1E PUSH DS
0001 2B C0 SUB AX,AX
0003 50 PUSH AX
0004 B8 ---- R MOV AX,DATASG
0007 8E D8 MOV DS,AX
0009 9A 0000 ---- E CALL SUBMUL ;Вызвать подпрограмму
000E CB RET
000F BEGIN ENDP
000F CODESG ENDS
END BEGIN
_____________________________________________________________________
Segments and Groups:
N a m e Size Align Combine Class
CODESG . . . . . . . . . . . . 000F PARA PUBLIC 'CODE'
DATASG . . . . . . . . . . . . 0004 PARA PUBLIC 'DATA'
STACKSG. . . . . . . . . . . . 0080 PARA STACK 'STACK'
Symbols:
N a m e Type Value Attr
BEGIN. . . . . . . . . . . . . F PROC 0000 CODESG Length=000F
PRICE. . . . . . . . . . . . . L WORD 0002 DATASG Global
QTY. . . . . . . . . . . . . . L WORD 0000 DATASG Global
SUBMUL . . . . . . . . . . . . L FAR 0000 External
page 60,132
TITLE SUBMUL Подпрограмма для умножения
EXTRN QTY:WORD,PRICE:WORD
;-------------------------------------------------
0000 CODESG SEGMENT PARA PUBLIC 'CODE'
0000 SUBMUL PROC FAR
ASSUME CS:CODESG
PUBLIC SUBMUL
0000 A1 0000 E MOV AX,PRICE
0003 8B 1E 0000 E MOV BX,QTY
0007 F7 E3 MUL BX ;Произведение в DX:AX
0009 CB RET
000A SUBMUL ENDP
000A CODESG ENDS
END SUBMUL
_____________________________________________________________________
Segments and Groups:
N a m e Size Align Combine Class
CODESG . . . . . . . . . . . . 000A PARA PUBLIC 'CODE'
Symbols:
N a m e Type Value Attr
PRICE. . . . . . . . . . . . . V WORD 0000 External
QTY. . . . . . . . . . . . . . V WORD 0000 External
SUBMUL . . . . . . . . . . . . F PROC 0000 CODESG Global Length=000A
_____________________________________________________________________
LINK
IBM Personal Computer Linker
Version 2.30 (C) Copyright IBM Corp 1981, 1985
Object Modules: B:CALLMUL3+B:SUBMUL3
Run File: [B:CALLMUL3.EXE]:
List File: [NUL.MAP]: CON
Libraries [.LIB]:
Start Stop Length Name Class
00000H 00019H 001AH CODESG CODE
00030H 00033H 0004H DATASG DATA
00040H 000BFH 0080H STACKSG STACK
PROGRAM entry point at 0000:0000
__________________________________________________________________________
Рис.21.5. Общие данные в подпрограмме.
Наличие общих данных предполагает возможность обработки в одном
ассемблерном модуле данных, которые определены в другом ассемблерном
модуле. Изменим предыдущий пример так, чтобы области QTY и PRICE
по-прежнему определялись в основной программе, но загрузка значений из
этих областей в регистры BX и AX выполнялась в подпрограмме. Такая
программа приведена на рис.21.5. В ней сделаны следующие изменения:
- В основной программе имена QTY и PRICE определены как PUBLIC.
Сегмент данных также определен с атрибутом PUBLIC. Обратите внимание
на атрибут Global (глобальный) для QTY и PRICE в таблице
идентификаторов.
- В подпрограмме имена QTY и PRICE определены как EXTRN и WORD.
Такое определение указывает ассемблеру на длину этих полей в 2 байта.
Теперь ассемблер сгенерирует правильный код операции для команд MOV,
а компоновщик установит значения операндов. Заметьте, что имена QTY и
PRICE в таблице идентификаторов имеют атрибут External (внешний).
Команды MOV в листинге подпрограммы имеют следующий вид:
A1 0000 E MOV AX,PRICE
8B 1E 0000 E MOV BX,QTY
В объектном коде шест.A1 обозначает пересылку слова из памяти в регистр
AX, а шест.8B - пересылку слова из памяти в регистр BX (объектный код для
операций с регистром AX чаще требует меньшее число байтов, чем с другими
регистрами). Трассировка выполнения программы показывает, что компоновщик
установил в объектном коде следующие операнды:
A1 0200
8B 1E 0000
Объектный код теперь идентичен коду сгенерированному в предыдущем примере,
где команды MOV находились в вызывающей программе. Это логичный результат,
так как операнды во всех трех программах базировались по регистру DS и
имели одинаковые относительные адреса.
Основная программа и подпрограмма могут определять любые другие
элементы данных, но общими являются лишь имеющие атрибуты PUBLIC и EXTRN.
Следуя основным правилам, рассмотренным в данной главе, можно теперь
компоновать программы, состоящие более чем из двух ассемблерных модулей и
обеспечивать доступ к общим данным из всех модулей. При этом следует
предусматривать стек достаточных размеров - в разумных пределах, для
больших программ определение 64 слов для стека бывает достаточным.
В гл.23 будет рассмотрены дополнительные свойства сегментов, включая
определение более одного сегмента данных и кодового сегмента в одном
ассемблерном модуле и использование директивы GROUP для объединения
сегментов в один общий сегмент.
ПЕРЕДАЧА ПАРАМЕТРОВ
________________________________________________________________
__________________________________________________________________________
page 60,132
TITLE CALLMULL4 (EXE) Передача параметров
; в подпрограмму
EXTRN SUBMUL:FAR
;-------------------------------------------------
0000 STACKSG SEGMENT PARA STACK 'Stack'
0000 40 [ ???? ] DW 64 DUP(?)
0080 STACKSG ENDS
;-------------------------------------------------
0000 DATASG SEGMENT PARA 'Data'
0000 0140 QTY DW 0140H
0002 2500 PRICE DW 2500H
0004 DATASG ENDS
;-------------------------------------------------
0000 CODESG SEGMENT PARA PUBLIC 'Code'
0000 BEGIN PROC FAR
ASSUME CS:CODESG,DS:DATASG,SS:STACKSG
0000 1E PUSH DS
0001 2B C0 SUB AX,AX