Jazik_Assemblera_dlja_IBM_PC_i_programmir
.pdf
|
|
|
|
+ |
ENDIF |
|
|
0108 |
A1 |
0102 R |
+ |
ENDIF |
AX,DIVDND |
;Загрузка делимого |
|
+ |
MOV |
||||||
0108 |
8B |
1E |
0104 R |
+ |
MOV |
BX,DIVSOR |
;Загрузка делителя |
010F |
2B |
C9 |
|
+ |
SUB |
CX,CX |
;Регистр для частного |
0111 |
3B |
C3 |
|
+ ??0000: |
CMP |
AX,BX |
;Делимое < делителя? |
0111 |
|
+ |
|||||
0113 |
72 |
05 |
|
+ |
JB |
??0001 |
; да - выйти |
0115 |
2B |
C3 |
|
+ |
SUB |
AX,BX |
;Делимое - делитель |
0117 |
41 |
|
|
+ |
INC |
CX |
|
0118 |
EB F7 |
|
+ |
JMP |
??0000 |
|
|
011A |
89 |
0E |
0106 R |
+ ??0001: |
MOV |
QUOTNT,CX |
;Запись результата |
011A |
+ |
||||||
= 0000 |
|
|
|
+ |
DIVIDE |
DIDND,DIVSOR,QUOT |
|
|
|
|
CNTR |
= 0 |
|
||
|
|
|
|
+ ; |
AX-делимое, BX-делитель, CX-частное |
||
|
|
|
|
+ |
IFNDEF |
DIDND |
|
= 0001 |
|
|
|
+ ; |
CNTR |
Делитель не определен |
|
|
|
|
+ |
= CNTR +1 |
|
||
|
|
|
|
+ |
ENDIF |
|
|
|
|
|
|
+ |
ENDIF |
QUOT |
|
|
|
|
|
+ |
IFNDEF |
|
|
= 0002 |
|
|
|
+ ; |
CNTR |
Частное не определено |
|
|
|
|
+ |
= CNTR +1 |
|
||
|
|
|
|
+ |
ENDIF |
CNTR |
|
|
|
|
|
+ |
IF |
|
|
|
|
|
|
+ ; |
EXITM |
Макрорасширение отменено |
|
011E |
C3 |
|
|
+ |
|
|
|
|
|
MAIN |
RET |
|
|
||
011F |
|
|
|
ENDP |
|
|
|
011F |
|
|
|
CSEG |
ENDS |
BEGIN |
|
|
|
|
|
|
END |
|
__________________________________________________________________________
Рис.20.6. Использование директив IF и IFNDEF.
МАКРОС, ИСПОЛЬЗУЮЩИЙ IFIDN-УСЛОВИЕ
________________________________________________________________
Программа на рис.20.7 содержит макроопределение по имени MOVIF, которая генерирует команды MOVSB или MOVSW в зависимости от указанного параметра. Макрокоманду можно кодировать с параметром B (для байта) или W (для слова) для генерации команд MOVSB или MOVSW из MOVS.
Обратите внимание на первые два оператора в макроопределении:
MOVIF |
MACRO |
TAG |
|
|
|
|
|
|
|
|
|
IFIDN |
<&TAG>, |
|
|
|
|
|
|
|
|
Условная директива IFIDN сравнивает заданный параметр (предположительно |
B |
|||||||||
или W) со строкой B. |
Если значения идентичны, то ассемблер генерирует REP |
|||||||||
MOVSB. Обычное использование амперсанда (&) |
- |
для |
конкатенации, |
но |
в |
|||||
данном примере операнд |
без амперсанда не |
будет |
работать. |
Если |
в |
|
||||
макрокоманде не будет указан параметр B или W, |
то |
ассемблер |
сгенерирует |
|||||||
предупреждающий комментарий и команду MOVSB (по умолчанию). |
|
MOVIF: |
для |
|||||||
Примеры в кодовом сегменте трижды проверяют макрокоманду |
||||||||||
параметра B, для параметра W и для |
неправильного |
параметра. |
Не |
следует |
||||||
делать попыток выполнения данной программы в том виде, как |
она приведена |
|||||||||
на рисунке, так как регистры CX и DX не обеспечены правильными значениями. |
||||||||||
Предполагается, что рассматриваемая макрокоманда |
не |
является |
очень |
|||||||
полезной и ее назначение здесь - проиллюстрировать |
условные |
директивы |
в |
простой форме. К данному моменту, однако, вы имеете достаточно информации для составления больших полезных макроопределений.
__________________________________________________________________________
|
|
TITLE |
MACRO7 (COM) Проверка директивы IFIDN |
|
|
|
; ------------------------------------------- |
MACRO |
TAG |
|
|
MOVIF |
||
|
|
|
IFIDN |
<&TAG>, |
|
|
|
REP MOVSB |
|
|
|
|
EXITM |
|
|
|
|
ENDIF |
<&TAG>, |
|
|
|
IFIDN |
|
|
|
|
REP MOVSW |
|
|
|
; |
ELSE |
|
|
|
Не указан параметр B или W, |
||
|
|
; |
по умолчанию принято B |
|
|
|
|
REP MOVSB |
|
|
|
|
ENDIF |
|
|
|
; |
ENDM |
|
0000 |
|
SEGMENT PARA 'Code' |
||
|
CSIG |
|||
|
|
|
ASSUME |
CS:CSEG,DS:CSEG |
0100 |
|
|
ASSUME |
SS:CSEG,ES:CSEG |
EB 00 |
BEGIN: |
ORG |
100H |
|
0100 |
JMP |
SHORT MAIN |
||
0102 |
|
; |
... |
NEAR |
|
MAIN |
PROC |
||
|
|
|
.LALL |
B |
|
|
+ |
MOVIF |
|
0102 |
F3/A4 |
IFIDN |
, |
|
+ |
REP MOVSB |
|||
|
|
+ |
EXITM |
W |
|
|
+ |
MOVIF |
|
|
|
ENDIF |
, |
|
0104 |
F3/A5 |
+ |
IFIDN |
|
+ |
REP MOVSW |
|||
|
|
+ |
ENDIF |
|
|
|
+ |
MOVIF |
|
|
|
ENDIF |
|
|
|
|
+ |
ELSE |
|
|
|
+ ;Не указан парам. B или W, по умолч.принято B |
||
0106 |
F3/A4 |
+ ;-------------------------------------------- |
REP MOVSB |
|
+ |
||||
0108 |
C3 |
+ |
ENDIF |
|
MAIN |
RET |
|
||
0109 |
|
ENDP |
|
|
0109 |
|
CSEG |
ENDS |
BEGIN |
|
|
|
END |
__________________________________________________________________________
Рис.20.7. Использование директивы IFIDN
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ
________________________________________________________________
-Макросредства возможны только для полной версии ассемблера (MASM).
-Использование макрокоманд в программах на ассемблере дает в результате более удобочитаемые программы и более производительный код.
-Макроопределение состоит из директивы MACRO, блока из одного или нескольких операторов, которые генерируются при макрорасширениях и директивы ENDM для завершения определения.
-Код, который генерируется в программе по макрокоманде, представляет
собой макрорасширение.
-Директивы .SALL, .LALL и .XALL позволяют управлять распечаткой комментариев и генерируемого объектного кода в макрорасширении.
-Директива LOCAL позволяет использовать имена внутри макроопределений. Директива LOCAL кодируется непосредственно после директивы MACRO.
-Использование формальных параметров в макроопределении позволяет кодировать параметры, обеспечивающие большую гибкость макросредств.
-Библиотека макроопределений дает возможность использовать макрокоманды для различных ассемблерных программ.
-Условные директивы позволяют контролировать параметры макрокоманд.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ
________________________________________________________________
20.1.Напишите необходимые директивы: а) для подавления всех команд, которые генерирует макрокоманда и б) для распечатки только команд, генерирующих объектный код.
20.2.Закодируйте два макроопределения для умножения: а) MULTBY должна генерировать код для умножения байта на байт; б) MULTWD должна генерировать код для умножения слова на слово. Для множителя и множимого используйте в макроопределении формальные параметры. Проверьте выполнение макрокоманд на небольшой программе, в которой также определены необходимые области данных.
20.3.Запишите макроопределения из вопроса 20.2 в "макробиблиотеку". Исправьте программу для включения элементов библиотеки по директиве INCLUDE в первом проходе ассемблирования.
20.4.Напишите макроопределение BIPRINT, использующей BIOS INT 17H для печати. Макроопределение должно включать проверку состояния принтера и обеспечивать печать любых строк любой длины.
20.5.Измените макроопределение на рис.20.6 для проверки делителя на ноль (для обхода деления).
ГЛАВА 21 Компоновка программ
__________________________________________________________________________
Ц е л ь: Раскрыть технологию программирования, включающую компоновку и выполнение ассемблерных программ.
ВВЕДЕНИЕ
________________________________________________________________
Примеры программ в предыдущих главах состояли |
из |
одного |
шага |
||
ассемблирования. |
Возможно, |
однако, выполнение программного |
модуля, |
||
состоящего из |
нескольких |
ассемблированных программ. |
В |
этом |
случае |
программу можно рассматривать, как состоящую из основной программы и одной или более подпрограмм. Причины такой организации программ состоят в следующем:
- бывает необходимо скомпоновать программы, написанные на разных языках, например, для объединения мощности языка высокого уровня и
эффективности ассемблера;
-программа, написанная в виде одного модуля, может оказаться слишком большой для ассемблирования;
-отдельные части программы могут быть написаны разными группами программистов, ассемблирующих свои модули раздельно;
-ввиду возможно большого размера выполняемого модуля, может появиться необходимость перекрытия частей программы в процессе выполнения.
Каждая программа ассемблируется отдельно и генерирует собственный уникальный объектный (OBJ) модуль. Программа компоновщик (LINK) затем компонует объектные модули в один объединенный выполняемый (EXE) модуль. Обычно выполнение начинается с основной программы, которая вызывает одну или более подпрограмм. Подпрограммы, в свою очередь, могут вызывать другие подпрограммы.
На рис.21.1 показаны два примера иерархической структуры основной подпрограммы и трех подпрограмм. На рис.21.1.(а) основная программы вызывает подпрограммы 1, 2 и 3. На рис.21.1.(б) основная программа вызывает подпрограммы 1 и 2, а подпрограмма 1 вызывает подпрограмму 3.
Существует много разновидностей организации подпрограмм, но любая организация должна быть "понятна" и ассемблеру, и компоновщику, и этапу выполнения. Следует быть внимательным к ситуациям, когда, например, под программа 1 вызывает подпрограмму 2, которая вызывает подпрограмму 3 и, которая в свою очередь вызывает подпрограмму 1. Такой процесс, известный как рекурсия, может использоваться на практике, но при неаккуратном обращении может вызвать любопытные ошибки при выполнении.
__________________________________________________________________________
a) ------------ |
|
¬ |
б) ------------ |
|
|
¬ |
¦ Основная |
¦ |
¦ Основная |
|
¦ |
||
¦ программа ¦ |
¦ программа ¦ |
|||||
L----- |
T------ |
¬ |
L----- |
T------ |
¬ |
|
---------- |
+--------- |
-----+---- |
¬ |
|||
----.---¬ ----.--- |
¬ ----.---¬ |
----.--- |
¬ ----.--- |
|||
¦ П/П 1 ¦ ¦ П/П 2 ¦ ¦ П/П 3 ¦ |
¦ П/П 1 ¦ ¦ П/П 2 ¦ |
|||||
L-------- |
L-------- |
L-------- |
L---T---- |
L-------- |
|
|
|
|
|
----.--- |
¬ |
|
|
|
|
|
¦ П/П 3 ¦ |
|
|
|
|
|
|
L-------- |
|
|
|
__________________________________________________________________________
Рис.21.1. Иерархия программ.
МЕЖСЕГМЕНТНЫЕ ВЫЗОВЫ
________________________________________________________________
Команды CALL в предыдущих главах использовались для внутрисегментных вызовов, т.е. для вызовов внутри одного сегмента. Внутрисегментный CALL может быть короткий (в пределах от +127 до -128 байт) или длинный ( превышающий указанные границы). В результате такой операции "старое" значение в регистре IP запоминается в стеке, а "новый" адрес перехода загружается в этот регистр.
Например, внутрисегментный CALL может иметь следующий объектный код: E82000. Шест.E8 представляет собой код операции, которая заносит 2000 в виде относительного адреса 0020 в регистр IP. Затем процессор объединяет текущий адрес в регистре CS и относительный адрес в регистре IP для получения адреса следующей выполняемой команды. При возврате из процедуры команда RET восстанавливает из стека старое значение в регистре IP и передает управление таким образом на следующую после CALL команду.
Вызов в другой кодовый сегмент представляет собой межсегментный (длинный) вызов. Данная операция сначала записывает в стек содержимое регистра CS и заносит в этот регистр адрес другого сегмента, затем
записывает в стек значение регистра IP и заносит новый относительный адрес в этот регистр.
Таким образом в стеке запоминаются и адрес кодового сегмента и смещение для последующего возврата из подпрограммы.
Например, межсегментный CALL может состоять из следующего объектного кода:
9A 0002 AF04
Шест.9A представляет собой код команды межсегментного вызова которая записывает значение 0002 в виде 0200 в регистр IP, а значение AF04 в виде 04AF в регистр CS. Комбинация этих адресов указывает на первую выполняемую команду в вызываемой подпрограмме:
Кодовый сегмент |
04AF0 |
Смещение в IP |
0200 |
Действительный адрес |
04CF0 |
При выходе из вызванной процедуры межсегментная команда возврата REP восстанавливает оба адреса в регистрах CS и IP и таким образом передает управление на следующую после CALL команду.
АТРИБУТЫ EXTRN и PUBLIC
________________________________________________________________
Рассмотрим основную |
программу |
(MAINPROG), |
которая |
вызывает |
||||
подпрограмму (SUBPROG) с помощью |
межсегментного |
CALL, |
как |
показано |
на |
|||
рис.21.2. |
|
|
|
SUBPROG |
существует |
вне |
||
Команда CALL в MAINPROG должна "знать", что |
||||||||
данного сегмента (иначе ассемблер выдаст сообщение |
о |
|
том, |
что |
||||
идентификатор SUBPROG не |
определен). |
С помощью |
директивы |
EXTRN можно |
||||
указать ассемблеру, что |
ссылка |
на |
SUBPROG |
имеет |
сам |
атрибут |
FAR, |
|
т.е.определена в другом ассемблерном модуле. Так |
как |
ассемблер |
не |
|||||
имеет возможности точно определить такие ссылки, |
он генерирует "пустой" |
|||||||
объектный код для последующего заполнения его при компановке: |
|
|
||||||
9A 0000 ---- E |
|
|
|
|
|
|
|
Подпрограмма SUBPROG содержит директиву PUBLIC, которая указывает ассемблеру и компоновщику, что другой модуль должен "знать" адрес SUBPROG. В последнем шаге, когда оба модуля MAINPROG и SUBPROG будут успешно ассемблированы в объектные модули, они могут быть скомпонованы следующим образом:
Запрос компоновщика LINK: |
Ответ: |
Object Modules [.OBJ]: |
B:MAINPROG+B:SUBPROG |
Run File [filespec.EXE]: |
B:COMBPROG (или другое имя) |
List File [NUL.MAP]: |
CON |
Libraries [.LIB]: |
[return] |
Компоновщик устанавливает соответствия между адресами EXTRN в одном объектном модуле с адресами PUBLIC в другом и заносит необходимые относительные адреса. Затем он объединяет два объектных модуля в один выполняемый. При невозможности разрешить ссылки компоновщик выдает сообщения об ошибках. Следите за этими сообщениями прежде чем пытаться выполнить программу.
__________________________________________________________________________
¦--------------------------------- |
EXTRN |
¬ |
SUBPROG:FAR ¦ |
||
¦ |
MAINPROG: . |
¦ |
¦ |
. |
SUBPROG |
¦ |
¦ |
CALL |
¦ |
|
¦ |
. |
|
¦ |
¦ |
. |
|
¦ |
+-------------------------------- |
PUBLIC |
SUBPROG |
+ |
¦ |
¦ |
||
¦ SUBPROG: . |
|
¦ |
|
¦ |
. |
|
¦ |
¦ |
. |
|
¦ |
¦ |
RET |
|
¦ |
L--------------------------------- |
|
|
|
__________________________________________________________________________
Рис.21.2. Межсегментный вызов.
Директива EXTRN
-----------------
Директива EXTRN имеет следующий формат: EXTRN имя:тип [, ... ]
Можно определить более одного имени (до конца строки) или закодировать дополнительные директивы EXTRN. В другом ассемблерном модуле соответствующее имя должно быть определено и идентифицировано как PUBLIC. Тип элемента может быть ABS, BYTE, DWORD, FAR, NEAR, WORD. Имя может быть определено через EQU и должно удовлетворять реальному определению имени.
Директива PUBLIC
------------------
Директива PUBLIC указывает ассемблеру и компоновщику, что адрес указанного идентификатора доступен из других программ. Директива имеет следующий формат:
PUBLIC идентификатор [, ... ]
Можно определить более одного идентификатора (до конца строки) или закодировать дополнительные директивы PUBLIC. Идентификаторы могут быть метками (включая PROC-метки), переменными или числами. Неправильными идентификаторами являются имена регистров и EQU-идентификаторы, определяющие значения более двух байт.
Рассмотрим три различных способа компановки программ.
ПРОГРАММА: ИСПОЛЬЗОВАНИЕ ДИРЕКТИВ EXTRN и PUBLIC ДЛЯ МЕТОК
_______________________________________________________________
__________________________________________________________________________
|
|
TITLE |
page |
60,132 |
|
|
CALLMULL1 (EXE) Вызов подпрограммы умнож. |
||
|
|
; |
EXTRN |
SUBMUL:FAR |
0000 |
|
|
SEGMENT PARA STACK 'Stack' |
|
40 [ ???? ] |
STACKSG |
DW |
||
0000 |
STACKSG |
64 DUP(?) |
||
0080 |
|
|
ENDS |
|
0000 |
|
;----------------------------------------------- |
SEGMENT PARA 'Data' |
|
0140 |
DATASG |
|||
0000 |
QTY |
DW |
0140H |
|
0002 |
2500 |
PRICE |
DW |
2500H |
0004 |
|
DATASG |
ENDS |
|
0000 |
|
;----------------------------------------------- |
SEGMENT PARA 'Code' |
|
|
CODESG |
|||
0000 |
|
BEGIN |
PROC |
FAR |
0000 |
1E |
|
ASSUME |
CS:CODESG,DS:DATASG,SS:STACKSG |
|||
C0 |
PUSH |
DS |
|
|
|
||
0001 |
2B |
SUB |
AX,AX |
|
|
|
|
0003 |
50 |
---- R |
PUSH |
AX |
|
|
|
0004 |
B8 |
MOV |
AX,DATASG |
|
|||
0007 |
8E |
D8 |
MOV |
DS,AX |
|
;Загрузить стоимость |
|
0009 |
A1 |
0002 R |
MOV |
AX,PRICE |
|||
000C |
8B |
1E 0000 R |
MOV |
BX,QTY |
|
; и количество |
|
0010 |
9A |
0000 ---- E |
CALL |
SUBMUL |
|
;Вызвать подпрограмму |
|
0015 |
CB |
BEGIN |
RET |
|
|
|
|
0016 |
|
ENDP |
|
|
|
|
|
0016 |
|
CODESG |
ENDS |
BEGIN |
|
|
|
|
|
|
END |
|
|
|
|
Segments and Groups: |
Size |
Align |
|
Combine Class |
|||
|
|
N a m e |
|
||||
CODESG . . . . . . . . . . . . |
0016 |
PARA |
|
NONE |
'CODE' |
||
DATASG . . . . . . . . . . . . |
0004 |
PARA |
|
NONE |
'DATA' |
||
STACKSG. . . . . . . . . . . . |
0080 |
PARA |
|
STACK |
'STACK' |
||
Symbols: |
N a m e |
Type |
Value |
|
Attr |
|
|
|
|
|
Length=0016 |
||||
BEGIN. . . . . . . . . . . . . |
F PROC |
0000 |
|
CODESG |
|||
PRICE. . . . . . . . . . . . . |
L WORD |
0002 |
|
DATASG |
|
||
QTY. . . . . . . . . . . . . . |
L WORD |
0000 |
|
DATASG |
External |
||
SUBMUL . . . . . . . . . . . . |
L FAR |
0000 |
|
|
|||
|
|
TITLE |
page |
60,132 |
|
|
|
|
|
SUBMUL |
Подпрограмма для умножения |
||||
0000 |
|
;----------------------------------------------- |
|||||
|
CODESG |
SEGMENT PARA 'Code' |
|
||||
0000 |
|
SUBMUL |
PROC |
FAR |
|
|
|
|
|
|
ASSUME |
CS:CODESG |
|
||
0000 |
F7 |
E3 |
PUBLIC |
SUBMUL |
;AX-стоимость, BX-количество |
||
MUL |
BX |
||||||
0002 |
CB |
SUBMUL |
RET |
|
;Произведение в DX:AX |
||
0003 |
|
ENDP |
|
|
|
|
|
0003 |
|
CODESG |
ENDS |
SUBMUL |
|
|
|
|
|
|
END |
|
|
|
|
Segments and groups: |
Size |
Align |
|
Combine Class |
|||
|
|
N a m e |
|
||||
CODESG . . . . . . . . . . . . |
0003 |
PARA |
|
NONE |
'CODE' |
||
Symbols: |
N a m e |
Type |
Value |
|
Attr |
|
|
|
|
|
Clobal Length=0003 |
||||
SUBMUL . . . . . . . . . . . . |
F PROC |
0000 |
|
CODESG |
__________________________________________________________________________
LINK
IBM Personal Computer Linker
Version 2.30 (C) Copyright IBM Corp 1981, 1985
Object Modules: B:CALLMUL1+B:SUBMUL1
Run File: [B:CALLMUL1.EXE]:
List File:[NUL.MAP]: CON
Libraries [.LIB]:
Start |
Stop |
Length |
Name |
Class |
|
00000H |
00015H |
0016H |
CODESG |
CODE |
<--Примечание: 2 кодовых |
00020H |
00022H |
0003H |
CODESG |
CODE |
<-- сегмента |
00030H |
00033H |
0004H |
DATASG |
DATA |
|
00040H |
000BFH |
0080H |
STACKSG |
STACK |
|
Program entry point at 0000:0000
__________________________________________________________________________
Рис. 21.3. Использование директив EXTRN и PUBLIC.
Программа на рис.21.3 состоит из основной программы CALLMUL1 и подпрограммы SUBMUL1. В основной программе определены сегменты для стека, данных и кода. В сегменте данных определены поля QTY и PRICE. В кодовом сегменте регистр AX загружается значением PRICE, а регистр BX - значением QTY, после чего происходит вызов подпрограммы. Директива EXTRN в основной программе определяет SUBMUL как точку входа в подпрограмму.
Подпрограмма содержит директиву PUBLIC (после ASSUME), которая указывает компоновщику, что точкой входа для выполнения является метка SUBMUL. Подпрограмма выполняет умножение содержимого регистра AX (цена) на содержимое регистра BX (количество). Результат умножения вырабатывается в регистровой паре DX:AX в виде шест.002E 4000.
Так как подпрограмма не определяет каких-либо данных, то ей не требуется сегмент данных. Если бы подпрограмма имела сегмент данных, то только она одна использовала бы свои данные.
Также в подпрограмме не определен стековый сегмент, так как она использует те же стековые адреса, что и основная программа. Таким образом,
стек определенный в основной программе является |
доступным |
и |
в |
|||||
подпрограмме. |
Для компоновщика необходимо обнаружить по крайней мере один |
|||||||
стек и определение стека в основной программе является достаточным. |
после |
|||||||
Рассмотрим теперь таблицы идентификаторов, |
вырабатываемые |
|
||||||
каждого |
ассемблирования. |
Обратите |
внимание, что SUBMUL в |
таблице |
||||
идентификаторов для основной |
программы |
имеет атрибуты FAR |
и |
External |
||||
(внешний), |
а |
для подпрограммы - F (для FAR) и Global (глобальный). |
Этот |
|||||
последний атрибут указывает, |
что данное имя доступно из вне подпрограммы, |
|||||||
т.е. глобально. |
|
|
|
|
|
|
|
|
Карта компановки (в конце листинга) отражает организацию программы в |
||||||||
памяти. Заметьте, что здесь имеются два |
кодовых сегмента |
(для |
каждого |
|||||
ассемблирования) с разными |
стартовыми |
адресами. |
Последовательность |
расположения кодовых сегментов соответствует последовательности указанных для компановки объектных модулей (обычно основная программа указывается первой). Таким образом, относительный адрес начала основной программы - шест.00000, а подпрограммы - шест.00020.
При трассировке выполнения программы можно обнаружить, что команда CALL SUBMUL имеет объектный код
9A 0000 D413
Машинный код для межсегментного CALL - шест.9A. Эта команда сохраняет в стеке регистр IP и загружает в него значение 0000, сохраняет в стеке значение шест.13D2 из регистра CS и загружает в него шест.D413. Следующая выполняемая команда находится по адресу в регистровой паре CS:IP т.е. 13D40 плюс 0000. Обратите внимание, что основная программа начинается по адресу в регистре CS, содержащему шест.13D2, т.е. адрес 13D20. Из карты компановки видно, что подпрограмма начинается по относительному адресу шест.0020. Складывая эти два значения, получим действительный адрес кодового сегмента для подпрограммы:
Адрес в CS |
13D20 |
Смещение в IP |
0020 |
Действительный адрес |
13D40 |
Компоновщик определяет это значение точно таким же образом, и подставляет его в операнд команды CALL.
ПРОГРАММА: ИСПОЛЬЗОВАНИЕ ДИРЕКТИВЫ PUBLIC В КОДОВОМ СЕГМЕНТЕ
________________________________________________________________
__________________________________________________________________________
|
|
TITLE |
page |
60,132 |
Вызов подпрограммы умнож. |
|
|
|
CALLMUL2 (EXE) |
||||
|
|
; |
EXTERN |
SUBMUL:FAR |
|
|
0000 |
|
|
SEGMENT PARA STACK 'Stack' |
|||
40 [????] |
STACKSG |
|
||||
0000 |
STACKSG |
|
DW |
64 DUP(?) |
||
0080 |
|
|
ENDS |
|
|
|
0000 |
|
;---------------------------------------------- |
SEGMENT PARA 'Data' |
|
||
0140 |
DATASG |
|
||||
0000 |
QTY |
DW |
0140H |
|
|
|
0002 |
2500 |
PRICE |
DW |
2500H |
|
|
0004 |
|
DATASG |
ENDS |
|
|
|
0000 |
|
;---------------------------------------------- |
SEGMENT PARA PUBLIC 'Code' |
|||
|
CODESG |
|||||
0000 |
|
BEGIN |
PROC |
FAR |
|
|
0000 |
1E |
|
ASSUME |
CS:CODESG,DS:DATASG,SS:STACKSG |
||
|
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 |
A1 0002 R |
|
MOV |
AX,PRICE |
||
000C |
8B 1E 0000 R |
|
MOV |
BX,QTY |
; и количество |
|
0010 |
9A 0000 ---- E |
|
CALL |
SUBMUL |
;Вызвать подпрограмму |
|
0015 |
CB |
BEGIN |
RET |
|
|
|
0016 |
|
ENDP |
|
|
|
|
0016 |
|
CODESG |
ENDS |
BEGIN |
|
|
|
|
|
END |
|
|
|
________________________________________________________________________ |
||||||
Segments and Group: |
|
Size |
Align |
Combine Class |
||
|
N a m e |
|
||||
CODESG . . . . . . . . . . . . .0016 |
PARA |
PUBLIC |
'CODE' |
|||
DATASG . . . . . . . . . . . . .0004 |
PARA |
NONE |
'DATA' |
|||
STACKSG. . . . . . . . . . . . .0080 |
PARA |
STACK |
'STACK' |
|||
Symbols: |
|
Type |
Value |
Attr |
|
|
|
N a m e |
|
|
|||
BEGIN. . . . . . . . . . . . . F PROC |
0000 |
CODESG Lenght=0016 |
||||
PRICE. . . . . . . . . . . . . L WORD |
0002 |
DATASG |
|
|||
QTY. . . . . . . . . . . . . . L WORD |
0000 |
DATASG |
External |
|||
SUBMUL . . . . . . . . . . . . L FAR |
0000 |
|
||||
|
|
TITLE |
page |
60,132 |
|
|
|
|
SUBMUL2 |
Вызываемая подпрограмма умножения |
|||
0000 |
|
;---------------------------------------------- |
SEGMENT PARA |
PUBLIC 'CODE' |
||
|
CODESG |
|||||
0000 |
|
SUBMUL |
PROC |
FAR |
|
|
|
|
|
ASSUME |
CS:CODESG |
|
|
0000 |
F7 E3 |
|
PUBLIC |
SUBMUL |
;AX-стоимость, BX-количество |
|
|
MUL |
BX |
||||
0002 |
CB |
SUBMUL |
RET |
|
;Произведение в DX:AX |
|
0003 |
|
ENDP |
|
|
|
|
0003 |
|
CODESG |
ENDS |
SUBMUL |
|
|
|
|
|
END |
|
|
|
Segments and Groups: |
|
Size |
Align |
Combine Class |
||
|
N a m e |
|
||||
CODESG. . . . . . . . . . . . . |
0003 |
PARA |
PUBLIC |
'CODE' |
Symbols: |
Type |
Value |
Attr |
N a m e |
|||
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. Компоновщик размещает подпрограмму на ближайшей границе параграфа непосредственно после основной программы -