Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Jazik_Assemblera_dlja_IBM_PC_i_programmir

.pdf
Скачиваний:
43
Добавлен:
27.03.2015
Размер:
2 Mб
Скачать

 

 

 

 

+

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. Компоновщик размещает подпрограмму на ближайшей границе параграфа непосредственно после основной программы -

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]