Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Абель.docx
Скачиваний:
13
Добавлен:
26.11.2018
Размер:
569.84 Кб
Скачать

13D40 плюс 0000. Обратите внимание, что основная программа начинается по

адресу в регистре CS, содержащему шест.13D2, т.е. адрес 13D20. Из карты

компановки видно, что подпрограмма начинается по относительному адресу

шест.0020. Складывая эти два значения, получим действительный адрес

кодового сегмента для подпрограммы:

Адрес в CS 13D20

Смещение в IP 0020

Действительный адрес 13D40

Компоновщик определяет это значение точно таким же образом, и подставляет

его в операнд команды CALL.

ПРОГРАММА: ИСПОЛЬЗОВАНИЕ ДИРЕКТИВЫ PUBLIC В КОДОВОМ СЕГМЕНТЕ

________________________________________________________________

__________________________________________________________________________

page 60,132

TITLE CALLMUL2 (EXE) Вызов подпрограммы умнож.

EXTERN 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

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 RET

0016 BEGIN ENDP

0016 CODESG ENDS

END BEGIN

________________________________________________________________________

Segments and Group:

N a m e Size Align Combine Class

CODESG . . . . . . . . . . . . .0016 PARA PUBLIC 'CODE'

DATASG . . . . . . . . . . . . .0004 PARA NONE 'DATA'

STACKSG. . . . . . . . . . . . .0080 PARA STACK 'STACK'

Symbols:

N a m e Type Value Attr

BEGIN. . . . . . . . . . . . . F PROC 0000 CODESG Lenght=0016

PRICE. . . . . . . . . . . . . L WORD 0002 DATASG

QTY. . . . . . . . . . . . . . L WORD 0000 DATASG

SUBMUL . . . . . . . . . . . . L FAR 0000 External

page 60,132

TITLE SUBMUL2 Вызываемая подпрограмма умножения

;----------------------------------------------

0000 CODESG SEGMENT PARA PUBLIC 'CODE'

0000 SUBMUL PROC FAR

ASSUME CS:CODESG

PUBLIC SUBMUL

0000 F7 E3 MUL BX ;AX-стоимость, BX-количество

0002 CB RET ;Произведение в DX:AX

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]: <return>

List File: [NUL.MAP]: CON

Libraries [.LIB]: <return>

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]: <return>

List File: [NUL.MAP]: CON

Libraries [.LIB]: <return>

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

0003 50 PUSH AX

0004 B8 ---- R MOV A,DATASG

0007 8E D8 MOV DS,AX

0009 FF 36 0002 R PUSH PRICE

000D FF 36 0000 R PUSH QTY

0011 9A 0000 ---- E CALL SUBMUL ;Вызвать подпрограмму

0016 CB RET

0017 BEGIN ENDP

0017 CODESG ENDS

END BEGIN

_____________________________________________________________________

Segments and Groups:

N a m e Sise Align Combine Class

CODESG . . . . . . . . . . . . 0017 PARA NONE 'CODE'

DATASG . . . . . . . . . . . . 0004 PARA NONE 'DATA'

STACKSG. . . . . . . . . . . . 0080 PARA STACK 'STACK'

Symbols:

N a m e Type Value Attr

BEGIN. . . . . . . . . . . . . F PROC 0000 CODESG Length=0017

PRICE. . . . . . . . . . . . . L WORD 0002 DATASG

QTY. . . . . . . . . . . . . . L WORD 0000 DATASG

SUBMUL . . . . . . . . . . . . L FAR 0000 External

_____________________________________________________________________

page 60,132

TITLE SUBMUL Вызываемая подпрограмма умножения

0000 CODESG SEGMENT PARA PUBLIC 'Code'

0000 SUBMUL PROC FAR

ASSUME CS:CODESG

PUBLIC SUMBUL

0000 55 PUSH BP

0001 8P EC MOV BP,SP

0003 8B 46 08 MOV AX,[BP+8] ;Стоимость

0006 8B 5E 06 MOV BX,[BP+6] ;Количество

0009 F7 E3 MUL BX ;Произведение в DX:AX

000B 5D POP BP

000F SUMBUL ENDP

000F CODESG ENDS

END

_____________________________________________________________________

Segments and Groups:

N a m e Size Align Combine Class

CODESG . . . . . . . . . . . . 000F PARA PUBLIC 'CODE'

Symbols:

N a m e Type Value Attr

SUBMUL . . . . . . . . . . . . F PROC 0000 CODESG Global Length=000F

_____________________________________________________________________

LINK

IBM Personal Computer Linker

Version 2.30 (C) Copyright IBM Corp 1981, 1985

Object Modules: B:CALLMUL4+B:SUBMUL4

Run File: [B:CALLMUL4.EXE]: <return>

List File: [NUL.MAP]: CON

Libraries [.LIB]: <return>

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.6. Передача параметров.

Другим способом обеспечения доступа к данным из вызываемой

подпрограммы является передача параметров. В этом случае вызывающая

программа физически передает данные через стек. Каждая команда PUSH должна

записывать в стек данные размером в одно слово из памяти или из регистра.

Программа, приведенная на рис.21.6, прежде чем вызвать подпрограмму

SUBMUL заносит в стек значения из полей PRICE и QTY. После команды CALL

стек выглядит следующим образом:

... | 1600 | D213 | 4001 | 0025 | 0000 | C213 |

6 5 4 3 2 1

1. Инициализирующая команда PUSH DS заносит адрес сегмента в стек. Этот

адрес может отличаться в разных версиях DOS.

2. Команда PUSH AX заносит в стек нулевой адрес.

3. Команда PUSH PRICE заносит в стек слово (2500).

4. Команда PUSH QTY заносит в стек слово (0140).

5. Команда CALL заносит в стек содержимое регистра CS (D213)

6. Так как команда CALL представляет здесь межсегментный вызов,то в стек

заносится также содержимое регистра IP (1600).

Вызываемая программа использует регистр BP для доступа к параметрам в

стеке, но прежде она запоминает содержимое регистра BP, записывая его в

стек. В данном случае, предположим, что регистр BP содержит нуль, тогда

нулевое слово будет записано в вершине стека (слева).

Затем программа помещает в регистр BP содержимое из регистра SP, так

как в качестве индексного регистра может использоваться регистр BP, но не

SP. Команда загружает в регистр BP значение 0072. Первоначально регистр SP

содержал размер пустого стека, т.е. шест.80. Запись каждого слова в стек

уменьшает содержимое SP на 2:

| 0000 | 1600 | D213 | 4001 | 0025 | 0000 | C213 |

| | | | | | |

SP: 72 74 76 78 7A 7C 7E

Так как BP теперь также содержит 0072, то параметр цены (PRICE) будет

по адресу BP+8, а параметр количества (QTY) - по адресу BP+6. Программа

пересылает эти величины из стека в регистры AX и BX соответственно и

выполняет умножение.

Перед возвратом в вызывающую программу в регистре BP

восстанавливается первоначальное значение, а содержимое в регистре SP

увеличивается на 2, с 72 до 74.

Последняя команда RET представляет собой "длинный" возврат в

вызывающую программу. По этой команде выполняются следующие действия:

- Из вершины стека восстанавливается значение регистра IP (1600)

- Содержимое регистра SP увеличивается на 2, от 74 до 76.

- Из новой вершины стека восстанавливается значение регистра CS

(D213).

- Содержимое регистра SP увеличивается на 2 от 76 до 78.

Таким образом осуществляется корректный возврат в вызывающую

программу. Осталось одно небольшое пояснение. Команда RET закодирована как

RET 4

Параметр 4 представляет собой число байт в стеке использованных при

передаче параметров (два слова в данном случае). Команда RET прибавит этот

параметр к содержимому регистра SP, получив значение 7C. Таким образом, из

стека исключаются ненужные больше параметры. Будьте особенно внимательны

при восстановлении регистра SP - ошибки могут привести к непредсказуемым

результатам.

КОМПОНОВКА ПРОГРАММ НА BASIC-ИНТЕРПРЕТАТОРЕ И АССЕМБЛЕРЕ

________________________________________________________________

В руководстве по языку BASIC для IBM PC приводятся различные методы

связи BASIC-интерпретатора и программ на ассемблере. Для этого имеются две

причины: сделать возможным использование BIOS-прерываний через

ассемблерные модули и создать более эффективные программы. Цель данного

раздела - дать общий обзор по данному вопросу; повторять здесь технические

подробности из руководства по языку BASIC нет необходимости.

Для связи с BASIC ассемблерные программы кодируются, транслируются и

компонуются отдельно. Выделение памяти для подпрограмм на машинном языке

может быть либо внутри, либо вне 64 Кбайтовой области памяти, которой

ограничен BASIC. Выбор лежит на программисте.

Существует два способа загрузки машинного кода в память:

использование оператора языка BASIC - POKE или объединение скомпонованного

модуля с BASIC-программой.

Использование BASIC-оператора POKE

------------------------------------

Хотя это и самый простой способ, но он удобен только для очень

коротких подпрограмм. Способ заключается в том, что сначала определяется

объектный код ассемблерной программы по LST-файлу или с помощью отладчика

DEBUG. Затем шестнадцатиричные значения кодируются непосредственно в

BASIC-программе в операторах DATA. После этого с помощью BASIC-оператора

READ считывается каждый байт и оператором POKE заносится в память для

выполнения.

Компановка ассемблерных модулей

---------------------------------

С большими ассемблерными подпрограммами обычно проще иметь дело, если

они оттранслированы и скомпонованы как выполнимые (EXE) модули. Необходимо

организовать BASIC-программу и выполнимый модуль в рабочую программу. При

работе с BASIC-программой не забывайте пользоваться командой BSAVE (BASIC

save) для сохранения программы и BLOAD - для загрузки ее перед

выполнением.

Прежде чем кодировать BASIC- и ассемблерную программы, необходимо

решить, каким из двух способов они будут связаны. В языке BASIC возможны

два способа: функция USR и оператор CALL. В обоих способах регистры DS, ES

и SS на входе содержат указатель на адресное пространство среды BASIC.

Регистр CS содержит текущее значение, определенное последним оператором

DEF SEG (если он имеется). Стековый указатель SP указывает на стек,

состоящий только из восьми слов, так что может потребоваться установка

другого стеке в подпрограмме. В последнем случае необходимо на входе

сохранить значение указателя текущего стека, а при выходе восстановить

его. В обоих случаях при выходе необходимо восстановить значение

сегментных регистров и SP и обеспечить возврат в BASIC с помощью

межсегментного возврата RET.

Скомпонуйте ваш ассемблированный объектный файл так, что бы он

находился в старших адресах памяти. Для этого используется параметр HIGH

при ответе на второй запрос компоновщика, например, B:имя/HIGH. Затем с

помощью отладчика DEBUG необходимо загрузить EXE-подпрограмму и по команде

R определить значения в регистрах CS и IP: они показывают на стартовый

адрес подпрограммы. Находясь в отладчике укажите имя (команда N) BASIC и

загрузите его командой L.

Два способа связи BASIC-программы и EXE-подпрограммы - использование

операторов USR или CALL. Работая в отладчике, необходимо определить

стартовый адрес EXE-подпрограммы и, затем, указать этот адрес или в

операторе USRn или в CALL. В руководстве по языку BASIC для IBM PC

детально представлено описание функции USRn и оператора CALL с различными

примерами.

Программа: Компоновка BASIC и ассемблера

------------------------------------------

__________________________________________________________________________

LOAD"D:BASTEST.BAS

LIST

010 CLEAR ,32768!

020 ' для BLOAD

030 ' для DEFSEG

040 ' для точки входа в CALL

050 ' для вызова ASM-модуля

060 FOR N = 1 TO 5

070 INPUT "Hours "; H

080 INPUT "Rate "; R

090 W = H * R

100 PRINT "Wage = " W

110 NEXT N

120 END

_____________________________________________________________________

TITLE LINKBAS Ассемблерная подпрограмма, вызываемая из BASIC

CODESG SEGMENT PARA 'CODE'

ASSUME CS:CODESG

CLRSCRN PROC FAR

PUSH BP ;Сохранить BP

MOV BP,SP ;База списка параметров

MOV AX,0600H ;Функция прокрутки

MOV BH,07 ; всего

MOV CX,0000 ; экрана

MOV DX,184FH

INT 10H

POP BP

RET ;Завершить подпрограмму

CLRSCRN ENDP

CODESG ENDS

END

__________________________________________________________________________

Рис.21.7. Основная программа на языке BASIC и подпрограмма на

ассемблере.

Рассмотрим теперь простой пример компановки программы для

BASIC-интерпретатора и подпрограммы на ассемблере. В этом примере

BASIC-программа запрашивает ввод значений времени и расценки и выводит на

экран их произведение - размер зарплаты. Цикл FOR-NEXT обеспечивает

пятикратное выполнение ввода и затем программа завершается. Пусть BASIC-

программа вызывает ассемблерный модуль, который очищает экран.

На рис. 21.7 приведена исходная BASIC-программа и ассемблерная

подпрограмма. Обратите внимание на следующие особенности BASIC-программы:

оператор 10 очищает 32К байт памяти; операторы 20, 30, 40 и 50 временно

содержат комментарии.

Позже мы вставим BASIC-операторы для связи с ассемблерным модулем.

BASIC-программу можно сразу проверить. Введите команду BASIC и затем

наберите все пронумерованные операторы так, как они показаны в примере.

Для выполнения программы нажмите F2. Не забудьте сохранить текст программы

с помощью команды

SAVE "B:BASTEST.BAS"

Обратите внимание на следующие особенности ассемблерной подпрограммы:

- отсутствует определение стека, так как его обеспечивает BASIC;

программа не предусмотрена для отдельного выполнения и не может быть

выполнена;

- подпрограмма сохраняет в стеке содержимое регистра BP и

записывает значение регистра SP в BP;

- подпрограмма выполняет очистку экрана, хотя она может быть

изменена для выполнения других операций, таких как прокрутка экрана

вверх или вниз или установка курсора.

Все что осталось - это связать эти программы вместе. Следующие

действия предполагают, что системная дискета (DOS) находится на дисководе

A, а рабочие программы - на дисководе B:

1. Наберите ассемблерную подпрограмму, сохраните ее под именем

B:LINKBAS.ASM и оттранслируйте ее.

2. Используя компоновщик LINK, сгенерируйте объектный модуль, который

будет загружаться в старшие адреса памяти:

LINK B:LINKBAS,B:LINKBAS/HIGH,CON;

3. С помощью отладчика DEBUG загрузите BASIC - компилятор: DEBUG

BASIC.COM.

4. По команде отладчика R выведите на экран содержимое регистров.

Запишите значения в регистрах SS, CS и IP.

5. Теперь установите имя и загрузите скомпонованный ассемблерный модуль

следующими командами:

N B:LINKBAS.EXE

L

6. По команде R выведите на экран содержимое регистров и запишите

значения в CX, CS и IP.

7. Замените содержимое регистров SS, CS и IP значениями из шага 4. (Для

этого служат команды R SS, R CS и R IP).

8. Введите команду отладчика G (go) для передачи управления в BASIC. На

экране должен появиться запрос из BASIC-программы.

9. Для того, чтобы сохранить ассемблерный модуль, введите следующие

команды (без номеров операторов):

DEF SEG = &Hxxxx (значение в CS из шага 6)

BSAVE "B:CLRSCRN.MOD",0,&Hxx (значение в CX из шага 6)

Первая команда обеспечивает адрес загрузки модуля в память для

выполнения. Вторая команда идентифицирует имя модуля, относительную

точку входа и размер модуля. По второй команде система запишет модуль

на дисковод B.

10. Теперь необходимо модифицировать BASIC-программу для компоновки.

Можно загрузить ее сразу, находясь в отладчике, но вместо этого

наберите команду SYSTEM для выхода из BASIC и, затем, введите Q для

выхода из отладчика DEBUG. На экране должно появиться приглашение DOS

11. Введите команду BASIC, загрузите BASIC-программу и выведите ее на

экран:

BASIC

LOAD "B:BASTEST.BAS"

LIST

12. Измените операторы 20, 30, 40 и 50 следующим образом:

20 BLOAD "B:CLRSCRN.MOD"

30 DEF SEG = &Hxxxx (значение в CS из шага 6)

40 CLRSCRN = 0 (точка входа в подпрограмму)

50 CALL CLRSCRN (вызов подпрограммы)

13. Просмотрите, выполните и сохраните измененную BASIC-программу.

Если BASIC-программа и ассемблерные команды были введены правильно, а

также правильно установлены шестнадцатеричные значения из регистров, то

связанная программа должна сразу очистить экран и выдать запрос на ввод

времени и расценки. На рис.21.8 приведен протокол всех шагов - но

некоторые значения могут отличаться в зависимости от версии операционной

системы и размера памяти.

Приведенный пример выбран намеренно простым только для демонстрации

компоновки. Можно использовать более сложную технологию, используя

передачу параметров из BASIC-программы в ассемблерную подпрограмму с

помощью оператора

CALL подпрограмма (параметр-1,параметр-2,...)

Ассемблерная подпрограмма может получить доступ к этим параметрам,

используя регистр BP в виде [BP], как это делалось ранее на рис.21.3. В

этом случае необходимо определить операнд в команде RET, соответствующий

длине адресов параметров в стеке. Например, если оператор CALL передает

три параметра то возврат должен быть закодирован в виде RET 6.

__________________________________________________________________________

D>LINK