Глава 13. Расширенные инструкции
Турбо Ассемблер распознает все стандартные мнемоники инс-
трукций Intel, применимые к текущему выбранному процессору. Дан-
ная глава представляет собой обзор этих инструкций. Подробный
перечень данных инструкций вы можете найти в кратком справочном
руководстве.
Эффективная генерация кода: директивы SMART и NOSMART
Эффективная генерация кода означает, что Турбо Ассемблер мо-
жет определить, что можно использовать различные инструкции более
эффективно, чем вы это делаете. Например, в некоторых случаях
можно заменить инструкцию LEA более короткой и быстрой инструкци-
ей MOV:
LEA AX,lval
можно заменить на:
MOV AX,OFFSET lval
Турбо Ассемблер предусматривает директивы, которые позволяют
вам использовать эффективную генерацию кода. Эти директивы описа-
ны в следующей таблице:
Директивы эффективной генерации кода Таблица 13.1
------------------T----------------------------------------------¬
¦ Директива ¦ Значение ¦
+-----------------+----------------------------------------------+
¦ SMART ¦ Разрешает генерацию эффективного кода. ¦
¦ ¦ ¦
¦ NOSMART ¦ Запрещает генерацию эффективного кода. ¦
L-----------------+-----------------------------------------------
По умолчанию генерация эффективного кода разрешена. Однако
на генерацию эффективного кода влияют не только директивы SMART и
NOSMART, но и директива VERSION.
Примечание: О директиве VERSION подробнее рассказыва-
ется в Главе 3.
Генерация эффективного кода влияет на следующие ситуации ге-
нерации кода:
- Замена инструкции LEA инструкцией MOV, если операндом инс-
трукции LEA является простой адрес.
- Где это возможно, выполняется генерация булевских инструк-
ций со знаком,. Например, AND AX,+02 вместо AND AX,0002.
- Замена инструкции CALL FAR xxxx комбинацией инструкций
PUSH CS, CALL NEAR xxxx, где целевой адрес xxxx использует
тот же регистр CS.
Использование эффективных инструкций облегчает написание эф-
фективного кода. Некоторые стандартные инструкции Intel также
расширены таким образом, чтобы расширить их возможности и облег-
чить использование. Они обсуждаются в следующих разделах.
Расширенные инструкции перехода
Условные переходы, такие как JC или JE в процессорах 8086,
80186 и 80286 могут быть только ближними (NAER), то есть переход
выполняется в границах сегмента и на расстояние -128 байт +127
байт относительно текущего адреса. Это ограничение действует и
для условных инструкций цикла, таких как JCXZ или LOOP (на всех
процессорах фирмы Intel).
Там, где это необходимо, Турбо Ассемблер может генерировать
дополнительные последовательности переходов и обходить это огра-
ничение. Например, Турбо Ассемблер может преобразовать инструк-
цию:
JC xxx
в инструкции:
JNC temptag
JMP xxx
Вы можете разрешить данную дополнительную последовательность
переходов в помощью директивы JUMPS, и запретить ее директивой
NOJUMPS. По умолчанию Турбо Ассемблер не генерирует это средство.
Когда вы указывает директиву JUMPS, Турбо Ассемблер резерви-
рует достаточно места для дополнительных последовательностей пе-
рехода во всех условных переходах вперед. Когда определяется фак-
тическое расстояние перехода вперед, дополнительная последова-
тельность может не понадобиться. Когда это происходит, Турбо Ас-
семблер для заполнения лишнего пространства генерирует инструкции
NOP.
Чтобы избежать дополнительных инструкций NOP, вы можете:
- использовать переопределение условных переходов, диапазон
которых вам известен, например:
JC SHORT abc
ADD ax,ax
abc:
- задать параметр командной строки /m (подробнее о нем расс-
казывается в Главе 2).
Дополнительные инструкции цикла процессора 80386
В инструкциях цикла процессора 80386 в качестве регист-
ра-счетчика может использоваться регистр CX или ECX. Стандартные
мнемоники инструкций LOOP, LOOPE, LOOPZ, LOOPNE и LOOPNZ фирмы
Intel выбирают регистр-счетчик на основе того, является ли теку-
щий сегмент кода 32-битовым сегментом (тогда используется ECX)
или 16-битовым (используется регистр CX).
Турбо Ассемблер имеет специальные инструкции, которые увели-
чивают гибкость средства LOOP. Инструкции LOOP, LOOPE, LOOPWZ,
LOOPWNE и LOOPWNZ используют в качестве счетчика регистр CX, не-
зависимо от текущего сегмента. Аналогично, инструкции LOOPD,
LOOPDE, LOOPDZ, LOOPDNE и LOOPDNZ используют в качестве счетчика
регистр ECX.
Дополнительные инструкции ENTER и LEAVE
Инструкции ENTER и LEAVE используются для удаления из стека
кадра процедуры. В зависимости от того, является текущий сегмент
кода 16-битовым или 32-битовым, стандартные инструкции ENTER и
LEAVE будут модифицировать либо регистры BP и SP, либо EBP и ESP.
Если сегмент кода - это 32-битовый сегмент, а сегмент стека - 16-
битовый, то данные инструкции могут оказаться неподходящими.
В Турбо Ассемблере предусмотрены 4 дополнительные инструк-
ции, которые всегда выбирают конкретный размер стека, независимо
от размера сегмента кода. Инструкции ENTERW и LEAVEW всегда выби-
рают в качестве регистров кадра стека BP и SP, a ENTERD и LEAVED
- регистры EBP и ESP.
Дополнительные инструкции возврата
Стандартная инструкция RET генерирует код, который соответс-
твующим образом завершает текущую процедуру. Это включает в себя
генерацию кода завершения процедуры, которая использует интер-
фейсные соглашения языка высокого уровня. Даже если для процедуры
используются соглашения NOLANGUAGE, инструкция RET в зависимости
от описания процедуры (описана она как NEAR или FAR) будет гене-
рировать различный код. Для процедуры NEAR Турбо Ассемблер гене-
рирует дальнюю инструкцию возврата. Для процедуры FAR Турбо Ас-
семблер генерирует ближнюю инструкцию возврата. (Вне процедуры
всегда генерируется ближний возврат.)
Турбо Ассемблер включает в себя дополнительные инструкции,
которые позволяют задавать генерацию нужных инструкций возврата
(без кода завершения). Они перечислены в следующей таблице:
Инструкции возврата Таблица 13.2
------------------T---------------------------------------------¬
¦ Инструкция ¦ Функция ¦
+-----------------+---------------------------------------------+
¦ RETN ¦ Всегда генерирует ближний возврат. ¦
¦ ¦ ¦
¦ RETNF ¦ Всегда генерирует дальний возврат. ¦
¦ ¦ ¦
¦ RETCODE ¦ Генерирует возврат, тип которого соответ- ¦
¦ ¦ ствует текущей выбранной модели. Для модели ¦
¦ ¦ TINY, SMALL, COMPACT и TPASCAL генерируется ¦
¦ ¦ ближний возврат. Для модели MEDIUM, LARGE, ¦
¦ ¦ HUGE и TCHUGE - дальний возврат. ¦
L-----------------+----------------------------------------------
Расширенные инструкции PUSH и POP
Турбо Ассемблер поддерживает несколько расширений инструкций
PUSH и POP. Эти расширения существенно уменьшают объем ввода, не-
обходимого для задания расширенной последовательности инструкций
PUSH и POP.
Инструкции PUSH и POP с несколькими операндами
На одной строке вы можете задать несколько инструкций PUSH и
POP. Например:
PUSH ax
PUSH bx
PUSH cx
POP cx
POP bx
POP ax
можно записать как:
PUSH ax bx cx
POP cx bx ax
Чтобы Турбо Ассемблер распознавал наличие нескольких операн-
дов в этих инструкциях, нужно чтобы любой операнд не мог рассмат-
риваться как часть соседнего операнда, например инструкция:
PUSH foo [bx]
может привести к непредвиденному результату, поскольку foo,[bx] и
foo[bx] являются допустимыми выражениями. Чтобы эта инструкция
стала определенней, можно использовать круглые или квадратные
скобки, например:
PUSH [foo] [bx]
Использование в инструкциях PUSH и POP указателей
Стандартные инструкции PUSH и POP не могут сохранять в стеке
дальние указатели, которые требуют 4 байт в процессорах 8086,
80186 и 80286 и 6 байт в процессоре 80386.
Турбо Ассемблер допускает указание в инструкциях PUSH и POP
операнда-указателя размером DWORD для процессора 8086, 80186 и
80286 и QWORD для процессора 80386. Когда обнаруживаются такие
инструкции, Турбо Ассемблер генерирует инструкции PUSH и POP для
двух частей операнда.
Занесение в стек константа (процессор 8086)
Хотя процессоры 80186б 80286 и 80386 среди основного набора
инструкций содержат инструкцию PUSH, которая заносит в стек не-
посредственное значение, в процессоре 8086 такой инструкции нет.
Однако Турбо Ассемблер позволяет использовать в инструкции
PUSH константы и в процессоре 8086. При этом генерируется после-
довательность инструкций, имеющая тот же результат, что и инс-
трукция PUSH c операндом-константой для процессора 80186 и выше.
Примечание: Это средство вы можете использовать толь-
ко при разрешении генерации эффективного кода.
Последовательность инструкций, которую Турбо Ассемблер ис-
пользуется для выполнения операции PUSH с константой, имеет длину
около 10 байт. Они короче и выполняются быстрее, чем выполнение
той же функции, но их выполнение связано с потерей содержимого
регистра, например:
MOV ax, constant
PUSH ax
Данная последовательность имеет длину только 6 байт, но в
процессе этого теряется содержимое регистра AX.
Расширенные инструкции сдвига
При использовании процессор 8086 инструкции сдвига RCL, RCR,
ROL, ROR, SHL, SHR, SAL и SAR не могут воспринимать константу
циклического сдвига, отличную от 1. При работе на процессорах
80186, 80286 и 80386 можно использовать константу циклического
сдвига со значением до 255.
Когда Турбо Ассемблер обнаруживает инструкцию сдвига со зна-
чением константы, большим 1 (при выборе процессора 8086),он гене-
рирует соответствующее число инструкций сдвига со значением конс-
танты циклического сдвига 1. Например, инструкции:
.8086
SHL ax,4
генерируют последовательность:
SHL ax,1
SHL ax,1
SHL ax,1
SHL ax,1
Принудительное переопределение сегментов: инструкции SEGxx
В Турбо Ассемблере предусмотрены 6 инструкций, которые
приводят к генерации переопределений сегмента. Эти инструкции пе-
речислены в следующей таблице:
Инструкции переопределения сегмента Таблица 13.3
----------------T-----------------------------------------------¬
¦ Инструкция ¦ Значение ¦
+---------------+-----------------------------------------------+
¦ SEGCS ¦ Генерирует префиксный байт переопределения CS.¦
¦ ¦ ¦
¦ SEGSS ¦ Генерирует префиксный байт переопределения SS.¦
¦ ¦ ¦
¦ SEGDS ¦ Генерирует префиксный байт переопределения DS.¦
¦ ¦ ¦
¦ SEGES ¦ Генерирует префиксный байт переопределения ES.¦
¦ ¦ ¦
¦ SEGFS ¦ Генерирует префиксный байт переопределения FS.¦
¦ ¦ ¦
¦ SEGGS ¦ Генерирует префиксный байт переопределения GS.¦
L---------------+------------------------------------------------
Вы можете использовать эти инструкции в сочетании с такими
инструкциями, как XLATB, которые не требуют аргументов, но могут
использовать переопределение сегментов, например:
SEGCS XLATB
Заметим, что в большинстве инструкций имеется альтернативная
форма, при которой, чтобы указать на необходимость переопределе-
ния, вы можете задать пустой аргумент:
XLAT BYTE cs:[bx]
Приведенные два примера генерируют один и тот же код.
Дополнительные инструкции работы с флагами
Часто, чтобы улучшить эффективность и уменьшить размер кода,
можно упростить инструкции работы с флагами. Например, если
единственным желаемым результатом является установка конкретного
бита в AX, а флаги процессора, на которые влияет данная инструк-
ция, значение не имеют, инструкцию:
OR ax,1000h
можно упростить до инструкции:
OR, ah,10h
Турбо Ассемблер обеспечивает 4 дополнительных инструкции,
которые обеспечивают эти функциональные возможности. Они показаны
в следующей таблице:
Эффективные инструкции работы с флагами Таблица 13.4
------------------T----------------T-----------------------------¬
¦ Инструкция ¦ Функция ¦Соответствует инструкции ¦
+-----------------+----------------+-----------------------------+
¦ SETFLAG ¦ Установка бит ¦ OR ¦
¦ ¦ (бита) флага. ¦ ¦
¦ ¦ ¦ ¦
¦ MASKFLAG ¦ Размаскирование¦ AND ¦
¦ ¦ бит (бита) ¦ ¦
¦ ¦ флага. ¦ ¦
¦ ¦ ¦ ¦
¦ TESTFLAG ¦ Проверка бит ¦ TEST ¦
¦ ¦ (бита) флага. ¦ ¦
¦ ¦ ¦ ¦
¦ FLIPFLAG ¦ Дополнение бит ¦ XOR ¦
¦ ¦ (бита) флага. ¦ ¦
L-----------------+----------------+------------------------------
Эти инструкции можно использовать для улучшения модульности
записей, например:
FOO RECORD R0:1,R1:4,R2:3,R3:1
.
.
.
TESTFLAG AX,R0
В данном примере TESTFLAG будет генерировать наиболее эффек-
тивные инструкции, независимо от того, где в записи находится R0.
Дополнительные инструкции работы с битами полей
Турбо Ассемблер может генерировать специальные последова-
тельности инструкций для задания значений и извлечения значений
из битовых полей, заданных с помощью оператора RECORD. Это позво-
ляет вам писать код, независимый от фактического расположения по-
ля в записи. При использовании их в сочетании с оператором ENUM,
записи на языке ассемблера могут получить очень высокий уровень
модульности. Список данных инструкций приведен в следующей табли-
це:
Инструкции для установки и извлечения значения Таблица 13.5
----------------T-----------------------------------------------¬
¦ Инструкция ¦ Функция ¦
+---------------+-----------------------------------------------+
¦ SETFIELD ¦ Устанавливает значение поля записи. ¦
¦ ¦ ¦
¦ GETFIELD ¦ Извлекает значение из поля записи. ¦
L---------------+------------------------------------------------
Инструкция SETFIELD
Инструкция SETFIELD генерирует код, устанавливающий значение
поля записи. Она имеет синтаксис:
SETFIELD имя_поля регистр/память_приемник, регистр_источник
где "имя_поля" - имя поля записи, "регистр/память_приемник" для
SETFIELD представляет собой регистр или адрес в памяти типа BYTE
или WORD (DWORD для процессора 80386). "Регистр_источник" должен
быть регистром того же или меньшего размера. Если источник мень-
ше, чем приемник, то регистр-источник должен быть младшей частью
другого регистра, имеющего тот же размер, что и приемник. Этот
полный регистр называется рабочим регистром. Используйте данный
регистр для сдвига значения в регистре-источнике, благодаря чему
оно выравнивается по приемнику. Например:
FOO RECORD R0:1,R1:4,R2:3,R3:1
.
.
.
SETFIELD F1 AX,BL ; рабочим регистром является BX
SETFIELD F1 AX,BH ; недопустимо!
SETFIELD сдвигает регистр-источник, чтобы эффективно выров-
нять его на поле приемника, выполняет операцию OR и помещает ре-
зультат в регистр-приемник.
Примечание: Операция SETFIELD уничтожает все содержи-
мое рабочего регистра.
Чтобы выполнить свою функцию, инструкция SETFIELD генерирует
эффективную и расширенную последовательность инструкций XOR,
XCHG, ROL, ROR, OR и MOVZX.
Операция SETFIELD не пытается очистить целевое поле перед
выполнением над его значением операции OR. Если это необходимо,
вы можете очистить поле явным образом, используя инструкцию
MASKFLAG.
Инструкция GETFIELD
Инструкция GETFIELD извлекает данные из поля записи. Логика
ее работы противоположна инструкции SETFIELD. Она имеет следующий
синтаксис:
GETFIELD имя_поля регистр_приемник, регистр/память_источник
где "имя_поля" и "регистр_приемник" работают так же, как в опера-
ции SETFIELD. "Регистр/память_источник" можно использовать также,
как "регистр_источник" в операции SETFIELD, например:
FOO RECORD R0:1,R1:4,R2:3,R3:1
.
.
.
GETFIELD F1 AX,BL ; рабочим регистром является BX
GETFIELD F1 AX,BH ; недопустимо!
Примечание: Операция GETFIELD уничтожает все содержи-
мое рабочего регистра.
Операция GETFIELD извлекает значение поля, обнаруженное в
регистре-источнике или по адресу памяти, и устанавливает в это
значение соответствующую часть регистра-приемника. На другие ре-
гистры (кроме рабочего) и флаги процессора эта инструкция не вли-
яет.
Чтобы выполнить свою функцию, инструкция GETFIELD генерирует
эффективную и расширенную последовательность инструкций MOV,
XCHG, ROL и ROR.
Дополнительная быстрая инструкции непосредственного умножения
Для эффективной индексации массивов Турбо Ассемблер обеспе-
чивает специальную операцию непосредственного умножения. Инструк-
ция FASTIMUL решает типичную проблему, возникающую при создании
массива структур. Для процессора 8086 инструкция непосредственно-
го умножения недоступна. Даже на более развитых процессорах умно-
жение с использованием сдвигов и сложений выполняется в некоторых
обстоятельствах существенно быстрее, чем стандартная непосредс-
твенная инструкция IMUL. На основе текущего назначения процессора
инструкция Турбо Ассемблера FASTIMUL выбирает между наиболее эф-
фективной доступной последовательностью сдвигов и сложений и не-
посредственной операцией IMUL текущего процессора (если она име-
ется). Инструкция FASTIMUL имеет следующий синтаксис::
FASTIMUL регистр_приемник, регистр/память_источник, значение
Данная инструкция очень напоминает тернарную операцию IMUL,
доступную на процессорах 80186, 80286 и 80386. Регистр-приемник -
это регистр размером в слово (или двойное слово при работе на
процессора 80386). "Регистр/память_источник" - это регистр или
адрес в памяти, который должен соответствовать размеру приемника.
"Значение" - это фиксированная константа со знаком (множитель).
Инструкция FASTIMUL использует для выполнения своей функции
комбинацию инструкций IMUL, MOV, NEG, SHL, ADD и SUB. При этом
содержимое регистра-источника (или адреса памяти) теряется. Флаги
процессора не изменяются.
Расширение необходимых инструкций для процессора 80386
Процессор 80386 имеет возможность работы в 16- или 32-раз-
рядном режиме. Многие стандартные инструкции в этих разных режи-
мах имеют разный смысл. В Турбо Ассемблере размером инструкции
можно управлять с помощью используемых в выражениях переопределе-
ний SMALL и LARGE.
В общем случае, если вы в адресном выражении используете
SMALL и LARGE, операция управляет генерацией адресной части инс-
трукции в зависимости от того, должна она быть 16- или 32-разряд-
ной.
Примечание: Более подробно о переопределении размера с
помощью операций SMALL и LARGE рассказывается в Главе 5.
Когда SMALL или LARGE указывается вне адресной части выраже-
ния, то можно управлять тем, какая инструкция выполняется - 16-
или 32-битовая. В тех случаях, когда размер инструкции определя-
ется по типу операнда, Турбо Ассемблер сам выбирает размер инс-
трукции. Действие SMALL и LARGE показано в следующей таблице.
Примечание: Турбо Ассемблер выбирает размер инструк-
ции, используя SMALL и LARGE, только когда нет другой ин-
формации.
Действие инструкций SMALL и LARGE Таблица 13.6
------------------------------T---------------------------------¬
¦ Инструкция ¦ Действие ¦
+-----------------------------+---------------------------------+
¦ PUSH[SMALL/LARGE] сегм_рег ¦ Выбирает, какая форма сегментно-¦
¦ ¦ го регистра (16- или 32-разряд-¦
¦ ¦ ная) используется в инструкции¦
¦ ¦ PUSH. ¦
¦ ¦ ¦
¦ POP[SMALL/LARGE] сегм_рег ¦ Выбирает, какая форма сегментно-¦
¦ ¦ го регистра (16- или 32-разряд-¦
¦ ¦ ная) используется в инструкции¦
¦ ¦ POP. ¦
¦ ¦ ¦
¦ FSAVE[SMALL/LARGE] указ_пам ¦ Выбирает, какая форма состояния¦
¦ ¦ с плавающей точкой (16- или¦
¦ ¦ 32-разрядная) сохраняется. ¦
¦ ¦ ¦
¦ FRSTOR[SMALL/LARGE] указ_пам¦ Выбирает, какая форма состояния¦
¦ ¦ с плавающей точкой (16- или¦
¦ ¦ 32-разрядная) восстанавливается.¦
¦ ¦ ¦
¦ FSTENV[SMALL/LARGE] указ_пам¦ Выбирает, какая форма состояния¦
¦ ¦ с плавающей точкой (16- или¦
¦ ¦ 32-разрядная) записывается. ¦
¦ ¦ ¦
¦ FLDENV[SMALL/LARGE] указ_пам¦ Выбирает, какая форма состояния¦
¦ ¦ с плавающей точкой (16- или¦
¦ ¦ 32-разрядная) загружается. ¦
¦ ¦ ¦
¦ LGDT[SMALL/LARGE] указ_пам ¦ Выбирает, какая форма глобальной¦
¦ ¦ таблицы дескрипторов (16- или 32¦
¦ ¦ -разрядная) загружается. ¦
¦ ¦ ¦
¦ SGDT[SMALL/LARGE] указ_пам ¦ Выбирает, какая форма глобальной¦
¦ ¦ таблицы дескрипторов (16- или 32¦
¦ ¦ -разрядная) сохраняется. ¦
¦ ¦ ¦
¦ LIDT[SMALL/LARGE] указ_пам ¦ Выбирает, какая форма таблицы¦
¦ ¦ дескрипторов прерываний (16- или¦
¦ ¦ 32-разрядная) загружается. ¦
¦ ¦ ¦
¦ SIDT[SMALL/LARGE] указ_пам ¦ Выбирает, какая форма таблицы¦
¦ ¦ дескрипторов прерываний (16- или¦
¦ ¦ 32-разрядная) сохраняется. ¦
¦ ¦ ¦
¦ JMP[SMALL/LARGE] указ_пам ¦ Для адресов памяти размером в¦
¦ ¦ двойное слово (DWORD) выбирает¦
¦ ¦ между 16-битовым переходом JMP¦
¦ ¦ типа FAR и 32-битовым переходом¦
¦ ¦ JMP типа NEAR. ¦
¦ ¦ ¦
¦ CALL[SMALL/LARGE] указ_пам ¦ Для адресов памяти размером в¦
¦ ¦ двойное слово (DWORD) выбирает¦
¦ ¦ между 16-битовой инструкцией¦
¦ ¦ CALL типа FAR и 32-битовой инс-¦
¦ ¦ трукцией CALL типа NEAR. ¦
L-----------------------------+----------------------------------
Вызов процедур с кадрами стека
Турбо Ассемблер поддерживает расширенную форму инструкции
CALL, которая позволяет вам непосредственно вызывать процедуры,
использующие интерфейсные соглашения языков высокого уровня.
Аргументы процедурам, использующим интерфейсные соглашения
языков высокого уровня, передаются через стек в кадре стека. Пе-
ред вызовом процедуры вызывающая программа должна занести эти ар-
гументы в стек.
Используемые процедурой интерфейсные соглашения определяют
порядок, в котором аргументы должны заноситься в стек. Для проце-
дур Бейсика, Фортрана и Паскаля аргументы заносятся в стек в том
порядке, в котором они указываются. Для языков Си и С++ аргументы
заносятся в стек в обратном порядке.
Используемые процедурой интерфейсные соглашения определяют
также, должна удалять аргументы из стека после вызова процедуры
сама процедура или вызывающая программа. Языка Си и С++ требуют,
чтобы стек очищала вызывающая программа. Во всех других языках
процедура должна сама удалить аргументы из стека перед возвратом
управления.
Турбо Ассемблер с помощью расширенной инструкции CALL сам
выполняет за вас функции по занесению аргументов в стек в нужном
порядке и очисткой стека. Вызов процедуры с параметрами имеет
следующий синтаксис:
CALL выражение [язык] [,список_аргументов]
где "выражение" является целью инструкции CALL, "язык" задает ис-
пользуемые при вызове языковые соглашения. Если вы не задаете
язык, Турбо Ассемблер использует по умолчанию язык, заданный в
директиве MODEL.
Примечание: О директиве MODEL более подробнее расска-
зывается в Главе 7.
Аргументы, если они имеются, указываются после идентификато-
ра языка. Синтаксис каждого аргумента в списке совпадает с син-
таксисом, который используется в расширенных инструкциях PUSH и
POP. Вы можете разделить эти аргументы запятыми, например:
CALL test PASCAL,ax,es OFFSET buffer,blen
Поскольку в данном примере указан Паскаль, Турбо Ассемблер
будет заносить аргументы в стек в том порядке, в котором они ука-
заны. Этот пример эквивалентен следующим инструкциям:
PUSH ax
PUSH es OFFSET buffer
PUSH word PTR blen
CALL test
Вызов процедуры Си требует, чтобы аргументы заносились в
стек в обратном порядке. Турбо Ассемблер делает это автоматичес-
ки, поэтому вызов вида:
CALL test C,ax,es OFFSET buffer, word PTR blen
даст в результате следующий код:
PUSH word PTR blen
PUSH es OFFSET buffer
PUSH ax
CALL test
SUB sp,8
При вызове процедуры с аргументами аргументы в списке следу-
ет всегда указывать в том порядке, в котором они следуют в заго-
ловке процедуры. При необходимости Турбо Ассемблер изменяет их
порядок на обратный.
Нужно не забывать разделять аргументы запятыми, а компоненты
аргументов - пробелами. В зависимости от соглашений указанного
языка Турбо Ассемблер может заносить аргументы в стек в обратном
порядке, но порядок компонентов аргумента он не изменяет.
Если при вызове используются интерфейсные соглашения
NOLANGUAGE, Турбо Ассемблер при наличии любого аргумента сообщает
об ошибке. Хотя аргументы для процедуры NOLANGUAGE можно задать с
помощью директивы ARG, при вызове такой процедуры требуется явное
занесение аргументов в стек.
Вызываемые процедуры, содержащие RETURNS
Процедуры, определяющие некоторые из своих аргументов с по-
мощью ключевого слова RETURNS требуют отдельного рассмотрения.
Эти аргументы используются для возврата значений в вызывающую
программу. Таким образом, вызывающая программа всегда извлекает
их из стека. Чтобы способствовать передаче этих аргументов, зад-
анных в описании процедуры после директивы RETURNS, в Турбо Ас-
семблере нет специальных расширений инструкции CALL. Перед инс-
трукцией CALL вы должны явным образом занести аргументы в стек
(PUSH), а потом извлечь их их стека (POP).
Вызов процедур методов для объектов: CALL.METHOD
Инструкция CALL расширена таким образом, чтобы поддерживать
методы объектов. Вызов метода объекта может генерировать либо не-
посредственный вызов (для статических методов), либо косвенный
вызов (для виртуальных методов).
Поскольку вы можете использовать косвенный вызов, выполняю-
щие вызов инструкции могут нарушить содержимое некоторых регист-
ров. В этом случае, если вы используете вызов виртуального мето-
да, Турбо Ассемблер позволяет вам выбрать соответствующие регист-
ры.
Приведем синтаксис расширения CALL.METHOD:
CALL указатель_экземпляра METHOD [имя_объекта:]имя_метода
[USES [сегм_регистр:]регистр_смещения] [язык_и_аргументы]
где "указатель_экземпляра" должен описывать экземпляр объекта. В
режиме MASM часто невозможно определить имя объекта, связанного с
экземпляром. В этом случае Турбо Ассемблер позволяет использовать
поле "имя_объекта", благодаря чему вы можете задать имя экземпля-
ра объекта.
Поле "имя_метода" содержит имя метода, который должен вызы-
ваться для конкретного экземпляра объекта.
Примечание: О том, как задавать виртуальных или стати-
ческий метод, подробнее рассказывается в Главе 8.
Если метод является виртуальным,и требуется косвенный вызов,
инструкция CALL.METHOD выполняет косвенный вызов через регистры
ES:BX (ES:EBX для модели USE32 процессора 80386). Если вы хотите
использовать другие регистры, можно переопределить их с помощью
оператора USES. Поле "сегм_регистр" это необязательный используе-
мый сегментный регистр, а "регистр_смещения" - это регистр смеще-
ния, используемый для вызова.
Для объектов, описанных с помощью таблиц NEAR, CALL.METHOD
загружает только регистр смещения. Турбо Ассемблер предполагает,
что сегментный регистр всегда установлен в корректное значение.
Примечание: Хорошей практикой программирования явля-
ется соответствующий вызов методов с помощью косвенного
вызова, даже когда вы знаете, что вызываемый метод являет-
ся статическим. При модификации объектов методы могут из-
мениться со статических на виртуальные.
Поле "язык_и_аргументы" инструкции CALL.METHOD содержит
необязательные спецификации языка и аргументов, которые идентичны
по форме описанным в предыдущем разделе.
Вызов процедур методов для С++ и Паскаля требует обычно,
чтобы экземпляр объекта передавался в качестве аргумента в стеке.
Подробнее об этом рассказывается в Главе 18 и Главе 19.
Остаточная рекурсия для методов объектов: инструкция JMP.METHOD
Турбо Ассемблер обеспечивает инструкцию JMP.METHOD, соот-
ветствующую инструкции CALL.METHOD. Она имеет следующий син-
таксис:
JMP указатель_экземпляра METHOD [имя_объекта:]имя_метода
[USES [сегм_регистр:]регистр_смещения]
Инструкция JMP.METHOD полностью аналогична инструкции
CALL.METHOD, за исключением того, что она:
- генерирует вместо инструкции CALL инструкцию JMP;
- генерирует код завершения процедуры для очистки стека пе-
ред генерацией инструкции JMP.
Инструкция JMP.METHOD позволяет писать эффективный код
остаточной рекурсии (tail recursion). Она предназначена для заме-
ны общей ситуации, когда инструкция CALL.METHOD дается для
конкретного метода с последующей инструкцией RET.
Дополнительные инструкции для объектного программирования
При создании экземпляра объекта требуется инициализировать
указатель таблицы виртуальных методом объекта (если он имеется),
чтобы он указывал на корректную таблицу виртуальных методов. Ин-
струкция TBLINIT позволяет вам сделать это автоматически. Эта ин-
струкция имеет следующий синтаксис:
TBLINIT указатель_экземпляра_объекта
Поле "указатель_экземпляра__объекта" представляет собой ад-
рес объекта, указатель таблицы виртуальных методов которого нужно
инициализировать. Инструкция TBLINIT предполагает, что экземпляр
объекта должен иметь тип текущего объекта (другими словами, не-
посредственно предшествующее определение объекта определяет объ-
ектный тип, который инициализирует TBLINIT). Например:
TBLINIT DS:ST
инициализирует указатель таблицы виртуальных методов объекта DS:
SI (если она имеется).
Назад | Содержание | Вперед