щика необходимо обнаружить по крайней мере один стек и
определение стека в основной программе является достаточным.
Рассмотрим теперь таблицы иднтификаторов, вырабатываемые
после каждого ассемблирования. Обратите внимание, что SUBMUL
в таблице идентификаторов для основной программы имеет
атрибуты FAR и External (внешний), а для подпрограммы - F





Ассемблер для IBM PC. Глава 21 5


(для FAR) и Global (глобальный). Этот последний атрибут
указывает, что данное имя доступно из вне подпрограммы, т.е.
глобально.
Карта компановки (в конце листинга) отражает организацию
программы в памяти. Заметьте, что здесь имеются два кодовых
сегмента (для каждого ассемблирования) с разными стартовыми
адресами. Последовательность расположения кодовых сегментов
соответствует последовательности указанных для компа новки
объектных модулей (обычно основная программа указывается
первой). Таким образом, относительный адрес начала основной
программы - шест.00000, а подпрограммы - шест. 00020.

------------------------------------------------------------
------------------------------------------------------------
Рис. 21.3. Использование директив EXTRN и PUBLIC.

При трассировке выполнения программы можно обнаружить,
что команда 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 В КОДОВОМ СЕГМЕНТЕ
------------------------------------------------------------

Следующий пример на рис.21.4 представляет собой вариант
программы на рис.21.3. Имеется одно изменение в основной
программе и одно - в подпрограмме. В обоих случаях в
директиве SEGMENT используется атрибут PUBLIC:

CODESG SEGMENT PARA PUBLIC 'CODE'

------------------------------------------------------------
------------------------------------------------------------
Рис.21.4. Кодовый сегмент, определенный как PUBLIC.



Ассемблер для IBM PC. Глава 21 6



Рассмотрим результирующую карту компановки и ообъектный
код команды 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

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

ПРОГРАММА: ОБЩИЕ ДАННЫЕ В ПОДПРОГРАММЕ



Ассемблер для IBM PC. Глава 21 7


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

Наличие общих данных предполагает возможность обработки в
одном ассемблерном модуле данных, которые определены в
другом ассемблерном модуле. Изменим предыдущий пример так,
чтобы области QTY и PRICE по-прежнему определялись в
основной программе, но загрузка значений из этих областей в
регистры BX и AX выполнялась в подпрограмме. Такая программа
приведена на рис.21.5. В ней сделаны следующие изменения:

ъ В основной программе имена QTY и PRICE определены как
PUBLIC. Сегмент данных также определен с атрибутом
PUBLIC. Обратите внимание на атрибут Global (глобаль
ный) для QTY и PRICE в таблице идентификаторов.

ъ В подпрограмме имена QTY и PRICE определены как EXTRN и
WORD. Такое определение указывает ассемблеру на длину
этих полей в 2 байта. Теперь ассемблер сгенерирует
правильный код операции для команд MOV, а компановщик
установит значения операндов. Заметьте, что имена QTY и
PRICE в таблице идентификаторов имеют атрибут External
(внешний).

------------------------------------------------------------
------------------------------------------------------------
Рис.21.5. Общие данные в подпрограмме.

Команды 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.
Следуя основным правилам, рассмотренным в данной главе,
можно теперь компановать программы, состоящие более чем из
двух ассемблерных модулей и обеспечивать доступ к общим



Ассемблер для IBM PC. Глава 21 8


данным из всех модулей. При этом следует предусматривать
стек достаточных размеров - в разумных пределах, для больших
программ определение 64 слов для стека бывает достаточным.
В главе 23 будет рассмотрены дополнительные свойства
сегментов, включая определение более одного сегмента данных
и кодового сегмента в одном ассемблерном модуле и использова
ние директивы GROUP для объединения сегментов в один общий
сегмент.

ПЕРЕДАЧА ПАРАМЕТРОВ
------------------------------------------------------------

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





Ассемблер для IBM PC. Глава 21 9


Так как BP теперь также содержит 0072, то параметр цены
(PRICE) будет по адресу BP+8, а параметр количества (QTY) -
по адресу BP+6. Программа пересылает эти величины из стека в
регистры AX и BX соответственно и выполняет умножение.

------------------------------------------------------------
------------------------------------------------------------
Рис.21.6. Передача параметров.

Перед возвратом в вызывающую программу в регистре 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.
Выбор лежит на программисте.



Ассемблер для IBM PC. Глава 21 10


Существует два способа загрузки машинного кода в память:
использование оператора языка 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 с различными
примерами.



Ассемблер для IBM PC. Глава 21 11



Программа: Компановка 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;
- подпрограмма выполняет очистку экрана, хотя она может
быть изменена для выполнения других операций, таких как
прокрутка экрана вверх или вниз или установка курсора.

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

Все что осталось - это связать эти программы вместе.
Следующие действия предполагают, что системная дискета (DOS)
находится на дисководе A, а рабочие программы - на дисководе
B:

1. Наберите ассемблерную подпрограмму, сохраните ее под
именем B:LINKBAS.ASM и оттранслируйте ее.
2. Используя компановщик LINK, сгенерируйте объектный
модуль, который будет загружаться в старшие адреса
памяти:
LINK B:LINKBAS,B:LINKBAS/HIGH,CON;

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



Ассемблер для IBM PC. Глава 21 12


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-программа и ассемблерные команды были введены
правильно, а также правильно установлены шестнадцатеричные
значения из регистров, то связанная программа должна сразу
очистить экран и выдать запрос на ввод времени и расценки.




Ассемблер для IBM PC. Глава 21 13


На рис.21.8 приведен протокол всех шагов - но некоторые
значения могут отличаться в зависимости от версии
операционной системы и размера памяти.
Приведенный пример выбран намеренно простым только для
демонстрации компановки. Можно использовать более сложную
технологию, используя передачу параметров из BASIC-программы
в ассемблерную подпрограмму с помощью оператора

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

------------------------------------------------------------
------------------------------------------------------------
Рис.21.8. Этапы связи BASIC и ассемблера.

Ассемблерная подпрограмма может получить доступ к этим
параметрам, используя регистр BP в виде [BP], как это
делалось ранее на рис.21.3. В этом случае необходимо
определить операнд в команде RET, соответствующий длине
адресов параметров в стеке. Например, если оператор CALL
передает три параметра то возврат должен быть закодирован в
виде RET 6.

КОМПАНОВКА ПРОГРАММ НА ЯЗЫКЕ PASCAL И АССЕМБЛЕРЕ
------------------------------------------------------------

В данном разделе показано, как можно установить связь
между программами на языке PASCAL фирм IBM и MicroSoft с
программами на ассемблере. На рис.21.9 приведен пример связи
простой PASCAL-программы с ассемблерной подпрограммой.
PASCAL-программа скомпилирована для получения OBJ-модуля,
а ассемблерная программа оттранслирована также для получения
OBJ-модуля. Программа LINK затем компанует вместе эти два
OBJ-модуля в один выполнимый EXE-модуль.
В PASCAL-программе определены две переменные: temp_row и
temp_col, которые содержат введенные с клавиатуры значения
строки и колонки соответственно. Программа передает адреса
переменных temp_row и temp_col в виде парамтеров в
ассемблерную подпрограмму для установки курсора по этим
координатам. PASCAL-программа определяет также имя
ассемблерной подпрограммы в операторе procedure как
move_cursor и определяет два параметра, как extern
(внешние). Оператор в PASCAL-программе, который вызывает
ассемблерную программу по имени и передает парметры, имеет
следующий вид:
move_cursor (temp_row, temp_col);

Через стек передаются следующие величины: указатель блока
вызывающей программы, указатель на сегмент возврата,
смещение возврата и адреса двух передаваемых параметров.
Ниже показаны смещения для каждого элемента в стеке:

00 Указатель блока вызывающей программы
02 Указатель сегмента возврата



Ассемблер для IBM PC. Глава 21 14


04 Указатель смещения возврата
06 Адрес второго параметра
08 Адрес первого параметра

Так как ассемблерная подпрограмма будет использовать
регистр BP, то его необходимо сохранить в стеке для
последующего восстановления при возврате в вызывающую
PASCAL-программу. Заметьте, что этот шаг в вызываемой
подпрограмме аналогичен предыдущему примеру на рис.21.6.

------------------------------------------------------------
------------------------------------------------------------
Рис.21.9. Компановка PASCAL-ассемблер.

Регистр SP обычно адресует элементы стека. Но так как
этот регистр нельзя использовать в качестве индексного
регистра, то после сохранения старого значения регистра BP
необходимо переслать адрес из регистра SP в BP. Этот шаг
дает возможность использовать регистр BP в качестве
индексного регистра для доступа к элементам в стеке.
Следующий шаг - получить доступ к адресам двух параметров
в стеке. Первый переданный параметр (адрес строки) находится
в стеке по смещению 08, и может быть адресован по BP+08.
Второй переданный параметр (адрес столбца) находится в стеке
по смещению 06 и может быть адресован по BP+06.
Два адреса из стека должны быть переданы в один из
индексных регистров BX, DI или SI. В данном примере адрес
строки пересылается из [BP+08] в регистр SI, а затем
содержимое из [SI] (значение строки) пересылается в регистр
DH.
Значение столбца пересылается аналогичным способом в
регистр DL. Затем подпрограмма использует значения строки и
столбца в регистре DX при вызове BIOS для установки курсора.
При выходе подпрограмма восстанавливает регистр BP. Команда
RET имеет операнд, значение которого в два раза больше числа
параметров, в данном случае 2х2, или 4. Параметры автома
тически выводятся из стека и управление переходит в вызываю
щую программу.
Если в подпрограмме предстоит изменить сегментный регистр
то необходимо сохранить его значение командой PUSH на входе
и восстановить командой POP на выходе. Можно также использо
вать стек для передачи величин из подпрограммы в вызывающую
программу. Хотя рассмотренная подпрограмма не возвращает
каких-либо значений, в языке PASCAL предполагается, что
подпрограмма возращает одно слово в регистре AX или двойное
слово в регистровой паре DX:AX.
В результате компановки двух программ будет построена
карта компановки, в которой первый элемент PASCALL
представляет PASCALL-программу, второй элемент CODESEG (имя
сегмента кода) представляет ассемблерную подпрограмму. Далее
следует несколько подпрограмм для PASCALL-программы. Эта
довольно тривиальная программа занимает в результате




Ассемблер для IBM PC. Глава 21 15


шест.5720 байт памяти - более 20К. Компилирующие языки
обычно генерируют объектные коды значительно превышающие по
объему размеры компилируемой программы.

КОМПАНОВКА ПРОГРАММ НА ЯЗЫКЕ C И АССЕМБЛЕРЕ
------------------------------------------------------------

Трудность описания связи программ на языке C и
ассемблерных программ состоит в том, что различные версии
языка C имеют разные соглашения о связях и для более точной
информации следует пользоваться руководством по имеющейся
версии языка C. Здесь приведем лишь некоторые соображения,
представляющие интерес:

ъ Большинство версий языка C обеспечивают передачу
параметров через стек в обратной (по сравнению с
другими языками) последовательности. Обычно доступ,
например, к двум параметрам, передаваемым через стек,
осуществляется следующим образом:

MOV ES,BP
MOV BP,SP
MOV DH,[BP+4]
MOV DL,[BP+6]
...
POP BP
RET

ъ Некоторые версии языка C различают прописные и строчные
буквы, поэтому имя ассемблерного модуля должно быть
представленно в том же символьном регистре, какой
используют для ссылки C-программы.

ъ В некоторых версиях языка C требуется, чтобы ассемб
лерные программы, изменяющие регистры DI и SI, записы
вали их содержимое в стек при входе и восстанавливали
эти значения из стека при выходе.

ъ Ассемблерные программы должны возвращать значения, если
это необходимо, в регистре AX (одно слово) или в
регистровой паре DX:AX (два слова).

ъ Для некоторых версий языка C, если ассемблерная
программа устанавливает флаг DF, то она должна сбросить
его командой CLD перед возвратом.

ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ
------------------------------------------------------------

ъ В основной программе, вызывающей подпрограмму,
необходимо определять точку входа как EXTRN, а в
подпрограмме - как PUBLIC.




Ассемблер для IBM PC. Глава 21 16


ъ Будьте внимательны при использовании рекурсий, когда
подпрограмма 1 вызывает подпрограмму 2, которая в свою
очередь вызывает подпрограмму 1.

ъ Если кодовые сегменты необходимо скомпановать в один
сегмент, то необходимо определить их с одинаковыми
именами, одинаковыми классами и атрибутом PUBLIC.