Turbo Assembler 3.0. Руководство пользователя


Значения по умолчанию: когда необходимо загружать сегменты?


В некоторых случаях вызываемые из языка С++ функции Ассемб- лера могут использовать (загружать) для обращения к данным ре- гистры DS и/или ES. Полезно знать соотношение между значениями сегментных регистров при вызове из Borland C++, так как иногда Ассемблер использует преимущества эквивалентности двух сегментных регистров. Давайте рассмотрим значения сегментных регистров в тот момент, когда функция Ассемблера вызывается из Borland C++, а также соотношения между сегментными регистрами, и случаи, когда в функции Ассемблера требуется загружать один или более сегментных регистров.

При входе в функцию Ассемблера из Borland C++ регистры CS и DS имеют следующие значения, которые зависят от используемой мо- дели памяти (регистр SS всегда используется для сегмента стека, а ES всегда используется, как начальный сегментный регистр):

Значения регистров при входе в Ассемблер из Borland C++ Таблица 18.2 ------------------------------------------------------------¬ ¦ Модель CS DS ¦ +-----------------------------------------------------------+ ¦ Крохотная _TEXT DGROUP ¦ ¦ Малая _TEXT DGROUP ¦ ¦ Компактная _TEXT DGROUP ¦ ¦ Средняя имя_файла_TEXT DGROUP ¦ ¦ Большая имя_файла_TEXT DGROUP ¦ ¦ Громадная имя_файла_TEXT имя_вызывающего_файла_DATA¦ L------------------------------------------------------------

Здесь "имя_файла" - это имя модуля на Ассемблере, а "имя_вы- зывающего_файла" - это имя модуля Borland C++, вызывающего модуль на Ассемблере.

В крохотной модели памяти _TEXT и DGROUP совпадают, поэтому при входе в функцию содержимое регистра CS равно содержимому DS. При использовании крохотной, малой и компактной модели памяти при входе в функцию содержимое SS равно содержимому регистра DS.

Когда же в функции на Ассемблере, вызываемой из программы на языке С++, необходимо загружать сегментный регистр? Отметим для начала, что вам никогда не придется (более того, этого не следует делать) загружать регистры SS или CS: при дальних вызовах, пере- ходах или возвратах регистр CS автоматически устанавливается в нужное значение, а регистр SS всегда указывает на сегмент стека и в ходе выполнения программы изменять его не следует (если только вы не пишете программу, которая "переключает" стеки. В этом слу- чае вам нужно четко понимать, что вы делаете).


Регистр ES вы можете всегда использовать так, как это требу- ется. Вы можете установить его таким образом, чтобы он указывал на данные с дальним типом обращения, или загрузить в ES сег- мент-приемник для строковой функции.

С регистром DS дело обстоит иначе. Во всех моделях памяти Borland C++, кроме сверхбольшой, регистр DS при входе в функцию указывает на статический сегмент данных (DGROUP), и изменять его не следует. Для доступа к данным с дальним типом обращения всегда можно использовать регистр ES, хотя вы можете посчитать, что для этого временно нужно использовать регистр DS (если вы собираетесь осуществлять интенсивный доступ к данным), что исключит необходи- мость использования в вашей программе множества инструкций с пре- фиксом переопределения сегмента. Например, вы можете обратиться к дальнему сегменту одним из следующих способов:

. . . .FARDATA Counter DW 0 . . . .CODE PUBLIC _AsmFunction _AsmFunction PROC . . . mov ax,@FarData mov es,ax ; ES указывает на ; сегмент данных с ; дальним типом ; обращения inc es:[Counter] ; увеличить значение ; счетчика . . . _AsmFunction ENDP . . .

или иначе:

. . . .FARDATA Counter DW 0 . . . .CODE PUBLIC _AsmFunction _AsmFunction PROC . . . assume ds:@FarData mov ax,@FarDAta mov ds,ax ; DS указывает на ; сегмент данных с ; дальним типом ; обращения inc [Counter] ; увеличить значение ; счетчика assume ds:@Data mov ax,@Data mov dx,ax ; DS снова указывает ; на DGROUP . . . _AsmFunction ENDP . . .

Второй вариант имеет то преимущество, что при каждом обраще- нии к дальнему сегменту данных в нем не требуется переопределение ES:. Если для обращения к дальнему сегменту вы загружаете регистр DS, убедитесь в том, что перед обращением к другим переменным DGROUP вы его восстанавливаете (как это делается в приведенном примере). Даже если в данной функции на Ассемблере вы не обращае- тесь к DGROUP, перед выходом из нее все равно обязательно нужно восстановить содержимое DS, так как в Borland C++ подразумевает- ся, что регистр DS не изменялся.



При использовании в функциях, вызываемых из С++, сверхболь- шой модели памяти работать с регистром DS нужно несколько по-дру- гому. В сверхбольшой модели памяти Borland C++ совсем не исполь- зует DGROUP. Вместо этого каждый модуль имеет свой собственный сегмент данных, который является дальним сегментом относительно всех других модулей в программе (нет совместно используемого ближнего сегмента данных). При использовании сверхбольшой модели памяти на входе в функцию регистр DS должен быть установлен таким образом, чтобы он указывал на этот дальний сегмент данных модуля и не изменялся до конца функции, например:

. . . .FARDATA . . . .CODE PUBLIC _AsmFunction _AsmFunction PROC push ds mov ax,@FarData mov ds,ax . . . pop ds ret _AsmFunction ENDP . . .

Заметим, что исходное состояние регистра DS сохраняется при входе в функцию _AsmFunction с помощью инструкции PUSH и перед выходом восстанавливается с помощью инструкции POP. Даже в сверх- большой модели памяти Borland C++ требует, чтобы все функции сох- раняли регистр DS.


Содержание раздела