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

         

Register must be AL or AX


(Допустимо указание только регистра AL или AX)

Неверен операнд команды. Допускается использовать только ре- гистры AL и AX. Например:

IN CL,dx ; ошибка: в первом операнде команды IN ; допускается указывать только регистры AL и AX



Register must be DX


(Допустимо указание только регистра DX)

Неверен операнд команды. Допускается использовать только ре- гистр DX. Например:

IN AL,cx ; ошибка: вместо СХ должен быть указан регистр DX



Регистры


Имена регистров представляют регистры процессоров семейства 89086 и могут использоваться в составе выражения, например:

5+ax+7

При вычислении данного выражения получается значение ax+12, так как AX - это зарезервированный в Турбо Ассемблере идентифи- катор регистра. Идентификаторы регистров перечислены в следующем списке:

8086 AX, BX. CX, DX, SI, DI, DP, CS, DS, ES, SS

80186,80286 то же, что и для 8086

80386 регистры 8086, плюс EAX, EBX, ECX, EDX, ESI EDI, EBP, PS, GS, CR0, CR3, CR3, DR0, DR1, DR@, DR#, DR6, DR7

80486 регистры процессора 80386, плюс TR3, TR4, TR5





Relative jump out of range by __ bytes


(Адрес назначения условного перехода превышает допустимый предел на __ байт)

Адрес назначения в команде условного перехода находится вне допустимого диапазона, т.е. не принадлежит интервалу (-127, +128) от текущего адреса. В 32-битовом сегменте адрес назначения услов- ного перехода должен находиться в диапазоне от -32767 до +32768 байт от текущего адреса.



Relative quantity illegal


(Недопустимый относительный адрес)

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

DATA SEGMENT PUBLIC X DB 0 IF OFFSET X GT 127 ; на этапе ассемблирования ; адрес не известен



Reserved word used as symbol


(Зарезервированное слово используется в качестве имени иден- тификатора)

В программе пользователя определяется имя идентификатора, совпадающее с одним из зарезервированных слов Турбо Ассемблера. Программа будет ассемблироваться правильно, однако переопределять зарезервированные в Турбо Ассемблере слова не рекомендуется.



Ресурсы вашего пакета


Данный пакет содержит много ресурсов, которые могут вам по- мочь:

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

- Ответы на общие вопросы, обзор новых средств, информация об установке содержатся в файле README, который находится в основном каталоге.



Режим Ideal Турбо Ассемблера


Для тех, кто пытается приспособить для своих целей макроас- семблер MASM, эта глава является, вероятно, наиболее важной в данном руководстве. Кроме очень хорошей совместимости с синтак- сисом MASM, Турбо Ассемблер позволяет несколько сгладить неудоб- ства программирования с использованием MASM с помощью улучшенного режима (режима Ideal).

Кроме всего прочего режим Ideal позволяет вам, глядя только на исходный текст, определить, как будет вести себя выражение или операнд инструкции. При этом нет необходимости держать в памяти все тонкости MASM. С помощью улучшенного режима вы просто сможете писать ясные и четкие выражения, которые будут выполняться так, как это было задумано.

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

Благодаря строгой проверке типа, выражения режима Ideal Тур- бо Ассемблера гораздо более понятны и менее способствуют получе- нию непредвиденных результатов. В результате многие из проблем MASM, о которых мы предупреждали вас в предыдущих главах, исчеза- ют под зорким оком режима Ideal.

Pежим Ideal содержит также ряд средств, облегчающих процесс программирования как для новичков, так и для опытных пользовате- лей. Вот некоторые из таких средств:

* дублирование имен элементов во множественных структурах;

* сложные выражения HIGH и LOW;

* предсказуемая обработка директив EQU;

* корректная обработка сгруппированных сегментов данных;

* улучшенная содержательность директив;

* хорошо воспринимаемые выражения, заключенные в квадратные скобки.



Результаты функции в Турбо Паскале


В зависимости от типа результата функции Турбо Паскаля возв- ращают свои результаты различными способами.

Результаты функции скалярного типа

Результаты функции скалярных типов возвращаются в регистрах центрального процессора (ЦП). Байтовые значения возвращаются в регистре AL, значения размером в 2 байта - в регистре AX, 4-байтовые значения - в паре регистров DX:AX (старшее слово нахо- дится в регистре DX).

Результаты функции вещественного типа

Результаты используемого в Турбо Паскале 6-байтового прог- раммно эмулируемого вещественного типа возвращаются в трех ре- гистрах ЦП. Наиболее значащее (старшее) слово возвращается в DX, среднее - в BX, а наименее значащее - в AX.

Результаты функции типов сопроцессора 8087

Результаты типов, использующихся сопроцессором 8087, возвра- щаются в регистре вершины стека ST(0) (или просто ST).

Результаты функции строкового типа

Результаты строкового типа возвращаются во временной рабочей области, выделяемой Турбо Паскалем перед вызовом. Указатель даль- него типа на эту область заносится в стек перед занесением перво- го параметра. Заметим, что этот указатель не является частью списка параметров.

Примечание: Не удаляйте из стека полученный в резуль- тате указатель, так как Турбо Паскаль ожидает, что после вызова он будет доступен.

Результаты функции типа указатель

Результаты указатель возвращаются в паре регистров DX:AX (сегмент:смещение).



Rotate count must be constant or CL


(Счетчик в командах сдвига должен быть указан с помощью константы или регистра CL)

В команде сдвига или циклического сдвига указан операнд, от- личный от константы и регистра CL. Например:

ROL ax,DL ; ошибка: регистр DL нельзя указывать ; в качестве счетчика

В командах сдвига и циклического сдвига в качестве второго операнда можно указывать только константу или регистр CL.



Rotate count out of range


(Недопустимое значение для счетчика сдвига)

В команде сдвига или циклического сдвига второй операнд пре- вышает допустимое значение. Например:

.8086

SHL DL,3 ; ошибка: в командах процессора 8086 ; возможен сдвиг только на один разряд .286 ROR ax,40 ; ошибка: максимальное допустимое ; значение для счетчика сдвига равно 31

Максимальное допустимое значение для счетчика сдвига в ко- мандах процессора 8086 равно 1, для других процессоров это значе- ние может быть равно 31.



Руководство пользователя


Часть 1: "Использование Турбо Ассемблера":

Глава 1: "Начало работы в Турбо Ассемблере" сообщает о том, что необходимо сделать для установки Турбо Ассемблера в вашей системе.

Глава 2: "Использование директив и параметров" приводит в алфавитном порядке подробную информацию обо всех директивах Турбо Ассемблера, и показывает, как с их помощью можно управлять рабо- той Турбо Ассемблера.

Глава 3: "Общие принципы программирования" описывает отличие режимов Ideal и MASM, а также рассказывает о том, как использо- вать предопределенные идентификаторы, символы комментариев и т.д.

Глава 4: "Объектно-ориентированное программирование" расска- зывает как можно в языке Ассемблера использовать методы объектно- ориентированного программирования.

В Главе 5 "Использование выражений и значений идентификато- ров" описывается вычисление и определение выражений и операций.

Глава 6 "Выбор директив процессора и идентификаторов" расс- казывает, как генерировать код для конкретных процессоров.

Глава 7 "Использование моделей памяти программ и сегмента- ции" сообщает о моделях программ, генерации идентификаторов (символических имен), упрощенных определений сегментов и о поряд- ке сегментов.

В Главе 8: "Определение типов данных" поясняется как опреде- лять структуры, объединения, таблицы, встроенные записи и объек- ты.

В Главе 9: "Установка и использование счетчика адреса" опи- сывается как и для чего желательно использовать счетчик адреса, а также об определении таблиц.

В Главе 10: "Описание процедур" рассказывается о том как можно использовать различные типы процедур, а также как опреде- лять и использовать аргументы и локальные переменные.

В Главе 11: "Управление областью действия идентификаторов" обсуждается как можно ограничивать или расширять области, в кото- рой идентификатор имеет конкретное значение.

В Главе 12: "Определение данных" описываются простые дирек- тивы определения данных, рассказывается о создании экземпляров структур, объединений, записей, перечислимых типов данных и объ- ектов.


В Главе 13 " Расширенные инструкции" описываются расширенные инструкции Турбо Ассемблера.

Глава 14 "Использование макрокоманд" рассказывает о том как можно использовать в исходном коде макрокоманды.

В Главе 15 "Использование условных директив" обсуждаются ди- рективы, которые позволяет реализовать условное выполнение кода.

Глава 16: "Интерфейс с компоновщиком" описывает как можно при компоновке кода включать в него библиотеки и описывать иден- тификаторы как общедоступные.

Глава 17 "Генерация листинга" рассказывает о директивах уп- равления листингом Турбо Ассемблера и их использовании.

Глава 18: "Интерфейс Турбо Ассемблера с Borland C++" описы- вает, как использовать язык Ассемблера совместно с языком высоко- го уровня Borland C++.

Глава 19: "Интерфейс Турбо Ассемблера с Турбо Паскалем" со- общает вам, как связывать программу на Ассемблере с программой на Турбо Паскале; здесь также приводятся примеры программ.

Приложение A: "Примеры программ" приводит примеры различного типа программных структур.

Приложение В: "Сводный синтаксис Турбо Ассемблера" содержит краткое изложение синтаксиса Турбо Ассемблера" иллюстрирует выра- жения Турбо Ассемблера (для режимов MASM и Ideal) в модифициро- ванной записи Бэкуса-Наура (BNF).

Приложение С: "Вопросы совместимости" описывает отличия ре- жима Турбо Ассемблера MASM и макроассемблера MASM.

Приложение D: "Утилиты" описывает новую утилиту-преобразова- тель файлов .h в .ash, поставляемую с данным пакетом. Здесь со- держится информация об утилитах MAKE, TLINK, TLIB и THELP, а также информация о программах GREP, TCREF и OBJXREF находится в файлах на дистрибутивных дисках.

Приложение D: "Сообщения об ошибках" описывает все сообщения об ошибках, которые могут быть сгенерированы Турбо Ассемблером: информационные сообщения, сообщения о фатальных ошибках, сообще- ния уровня предупреждения и сообщения об ошибках.


Сan't locate file __


(Не обнаружен файл __)

В директиве INCLUDE указано имя несуществующего файла. О ди- рективе INCLUDE см. Главу 10 настоящего руководства, где описан алгоритм поиска Турбо Ассемблером включаемых файлов. Если выдано это сообщение, проверьте, правильно ли указано в имени файла имя дисковода и маршрут доступа.



Счетчик повторения макрокоманды


Для повторения тела макрокоманды заданное число раз вы може- те использовать директиву повторения REPT. Для этого используется следующий синтаксис:

REPT выражение тело_макрокоманды ENDM

где "выражение" указывает Турбо Ассемблеру, сколько раз нужно повторить тело макрокоманды, заданное между директивами REPT и ENDM. При вычислении "выражения" должна получаться константа. Оно не должно содержать имен идентификаторов с опережающими ссылками. Чтобы отметить конец блока повторения, используйте директиву ENDM. Например, следующий код:

REPT 4 SHL ax,1 ENDM

даст в результате следующее:

SHL ax,1 SHL ax,1 SHL ax,1 SHL ax,1



Segment alignment not strict enough


(Выравнивание сегмента не достаточно точное)

Указано недопустимое значение для границы выравнивания сег- мента. Либо оно не является степенью двойки, либо специфицирует более точное выравнивание чем то, которое указано в директиве SEGMENT. Например:

DATA SEGMENT PARA ALIGN 32 ; ошибка: PARA означает только 16 ALIGN 3 ; ошибка: не является степенью двойки



Segment attributes illegally redefined


(Недопустимое переопределение атрибутов сегмента)

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

DATA SEGMENT BYTE PUBLIC DATA ENDS DATA SEGMENT PARA ; ошибка: ранее было указано ; выравнивание на байт DATA ENDS

При повторном открытии сегмента атрибуты либо должны иметь те же самые значения, либо вообще быть опущены. Если при повтор- ном открытии сегмента атрибуты не указаны, то используются атри- буты из предыдущего определения.



Segment name is superfluous


(Имя сегмента игнорируется)

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



Сегменты и группы


Способ, с помощью которого Турбо Ассемблер обрабатывает в режиме Ideal сегменты и группы, может внести большое различие в формирование и выполнение программ. Как и большинству людей, вам, вероятно, не захочется ломать голову над ошибками, возникающими из-за взаимодействия сегментов и групп.

Большинство трудностей в этом процессе возникает из-за про- извольного характера предположений в MASM (и следовательно Турбо Ассемблером в режиме MASM) о ссылках на данные или код в группах. К счастью, режим Ideal сглаживает некоторые наиболее явные проб- лемы, которые могут вызвать директивы определения сегментов и групп в MASM. Об этом мы и расскажем далее.



Символ !


Символ ! позволяет вам вызывать макрокоманду с аргументами, которые содержат специальные символы. Указание этого символа пе- ред другим символом эквивалентно заключению этого второго символа в угловые скобки. Например, !; действует также, как <;>. Некото- рые общие случаи использования данного символа показаны в следую- щей таблице:

Использование специального символа ! Таблица 14.2 ------------------T---------------------------------------------¬ ¦ Строка ¦ Полученные в результате символ ¦ +-----------------+---------------------------------------------+ ¦ !> ¦ > ¦ ¦ ¦ ¦ ¦ !< ¦ < ¦ ¦ ¦ ¦ ¦ !! ¦ ! ¦ L-----------------+----------------------------------------------



Символ вычисления выражения %


Символ % указывает Турбо Ассемблеру, что выражение нужно вы- числить. Ассемблер преобразует результат выражения к виду ASCII с текущим основанием. Используйте данный символ, когда в качестве макроаргумента вы хотите передать строку,представляющую вычислен- ный результат, а не само выражение. При этом используется следую- щий синтаксис:

%выражение

где "выражение" может быть либо выражением (использующим любые допустимые операнды и операции), или именем текстовой макрокоман- ды. Если это выражение, то создаваемым текстом будет результат выражения с текущим основанием. Если "выражение" - это текстовая макрокоманда, то создаваемым тестом будет текст, который предс- тавляет текстовая макрокоманда. Например, следующий фрагмент программы:

DEFSYM MACRO NUM TMP_&NUM: ENDNUM

TNAME EQU <JUNK> ; определение ; текстовой макрокоманды DEFSYM %5+4 DEFSYM %TNAME

приведет с следующему макрорасширению:

TMP_9: TMP_JUNK:



Синтаксис директив ARG и LOCAL


Приведем синтаксис определения передаваемых процедуре аргу- ментов:

ARG аргумент [,аргумент] . [=идентификатор] [RETURNS аргумент] [,аргумент]]

При определении локальных переменных процедуры используется следующий синтаксис:

LOCAL аргумент [,аргумент] . [=идентификатор]

Отдельные аргументы имеют следующий синтаксис:

имя_аргумента [[выражение_счетчик_1]] [: сложный_тип [:выражение_счетчик_2]]

где "сложный_тип" - это тип данных аргумента. Он может быть либо простым типом, либо сложным выражением-указателем. Подробнее о синтаксисе сложных типов рассказывается в Главе 5.

Если вы не задаете поле "сложный_тип", Турбо Ассемблер пред- полагает WORD. При выборе 32-разрядной модели он предполагает DWORD.

"Выражение_счетчик_2" задает, сколько элементов данного типа определяет аргумент. Например, в определении аргумента:

ARG tmp:DWORD:4

определяется аргумент с именем "tmp", состоящий из 4 двойных слов.

По умолчанию "выражение_счетчик_2" имеет значение 1 (кроме аргументов типа BYTE. Так как вы не можете занести в стек байто- вое значение, для аргументов типа BYTE значение счетчика по умол- чанию равно 2, что обеспечивает для них в стеке размер в слово. Это согласуется с языками высокого уровня, которые интерпретируют передаваемые в качестве параметров символьные переменные. Если вы действительно хотите задать аргумент, как один байт в стеке, нуж- но явным образом определить значение поля "выражение_счетчик_2", равное 1. Например:

ARG realbyte:BYTE:1

"Выражение_счетчик_1" представляет собой число элементов массива. Общее пространство, резервируемое для аргумента в стеке, равно произведению "выражения_счетчик_2" на длину, заданную полем "тип_аргумента" и на "выражение_счетчик_1". Если поле "выражение_ счетчик_1" не задано, то по умолчанию оно равно 1. Общее число аргументов задает произведение "выражения"_счетчик_1" на "выраже- ние_счетчик_2".


Если вы завершаете список аргументов символом равенства (=) и идентификатором, то Турбо Ассемблер будет приравнивать этот идентификатор к общему размеру блока аргументов (в байтах). Если вы не используете автоматическое использование соглашений языков высокого уровня в Турбо Ассемблере, то можете использовать данное значение в конце процедуры в качестве аргумента инструкции RET. Заметим, что это вызывает очистку стека от всех занесенных туда перед возвратом аргументов (это соглашения по вызову, принятые в Паскале).
Аргументы и переменные определяются в процедуре как операнды в памяти относительно BP. Передаваемые аргументы, определенные с помощью директивы ARG, имеют положительное смещение относительно BP. Локальные переменные, определенные с помощью директивы LOCAL, имеют отрицательное смещение от BP. Приведем пример:
. . . func1 PROC NEAR ARG a:WORD,b:WORD:4,c:BYTE=d LOCAL x:DWORD,y=WORD:2=z . . .
Здесь a определяется, как [bp+4], b определяется, как [bp+6], c определяется, как [bp+14], а d - как 20. x - это [bp-2], y - [bp-6], а z - 8.

Синтаксис определения процедур


Для описания процедур вы можете использовать директиву PROC. В режиме Ideal она имеет следующий синтаксис:

PROC [[модификатор_языка] язык] имя [расстояние] [ARG список_аргументов] [RETURN список_элементов]; [LOCAL список_аргументов] [USES список_элементов] . . . ENDP [имя]

В режиме MASM используется следующий синтаксис:

имя PROC [[модификатор_языка] язык] [расстояние] [ARG список_аргументов] [RETURN список_элементов]; [LOCAL список_аргументов] [USES список_элементов] . . . [имя] ENDP

Турбо Ассемблер также воспринимает для определения процедур синтаксис MASM. Подробнее о синтаксисе MASM рассказывается в Гла- ве 3.



Синтаксис условных директив


Тремя директивами условного ассемблирования являются дирек- тивы IFxxx, ELSEIFxxx и ERRxxx. Эти директивы используются также, как операторы условия в языках высокого уровня.



Скалярные типы


Параметры-значения всех скалярных типов (boolean, char, shortint, byte, integer, word, longint, отрезки типов и перечис- лимые типы) передаются как значения через стек процессора. Если размер объекта составляет 1 байт, он заносится в стек, как полное 16-битовое слово, однако более значащий (старший) байт слова не содержит полезной информации. Если размер объекта равен двум бай- там, то он просто заносится в стек "как есть". Если объект имеет размер 4 байта (длинное целое), он заносится в стек, как два 16-битовых слова. В соответствии со стандартом процессоров серии 8088 наиболее значащее (старшее) слово заносится в стек первым и занимает в стеке старшие адреса.



Соглашения для конкретного языка


Когда вы назначаете имена идентификаторам, которые планиру- ется использовать глобально, нужно помнить об использовании спе- цификатора языка. К именам переменных предъявляются следующие требования:

- в Паскале имена указываются символами в верхнем регистре;

- в Си/С++ имен должны начинаться с символа подчеркивания (_). Остальная часть имени записывается символами в нижнем регистре.

Параметр /m (описанный в Главе 2) сообщает Турбо Ассемблеру, что во всех именах идентификаторов нужно различать регистр. Пара- метр /mx (также описанный в Главе 2) указывает, что различать ре- гистр символов нужно только во внешних и общедоступных идентифи- каторах, и что все другие идентификаторы в исходном файле записа- ны в верхнем регистре. Когда вы используете эти два параметра вместе, для идентификаторов, описанных в Паскале, они имеют спе- циальное значение: они приводят к тому, что рассматриваемые иден- тификаторы будут доступны компоновщику, как идентификаторы в верхнем регистре.



Соглашения по обозначениям


Когда мы говорим о компьютерах IBM PC и совместимых с ними, то речь идет о любых компьютерах, в которых используются процес- соры 8088, 8086, 80186, 80286, 386 и i486 (все эти процессоры обозначаются, как 80х86). Когда вы встречаете термин PC-DOS или MS-DOS, то речь идет о версии операционной системы 2.0 и старше. В данном руководстве используются следующие соглашения:

----------------T-----------------------------------------------¬ ¦ Обозначение ¦ Описание обозначения ¦ +---------------+-----------------------------------------------+ ¦ ¦ Столбец из точек перед строками, где описыва- ¦ ¦ . ¦ ется синтаксис или приводится пример програм- ¦ ¦ . ¦ мы, говорит о том, что фрагмент программы ¦ ¦ . ¦ опущен. ¦ ¦ ¦ ¦ ¦ образец ¦ Слова, указанные в примерах строчными буква- ¦ ¦ ¦ ми, показывают, что вместо них должны быть ¦ ¦ ¦ подставлены значения. Например, ниже при- ¦ ¦ ¦ веден синтаксис оператора ОFFSET: ¦ ¦ ¦ ¦ ¦ ¦ OFFSET выражение ¦ ¦ ¦ ¦ ¦ ¦ Он показывает, что за оператором OFFSET мо- ¦ ¦ ¦ жет следовать любое выражение. При записи ¦ ¦ ¦ исходного кода в соответствии с этим синтак- ¦ ¦ ¦ сисом вы можете записать: ¦ ¦ ¦ ¦ ¦ ¦ OFFSET here+6 ¦ ¦ ¦ ¦ ¦ ¦ где here+6 является выражением. ¦ ¦ ¦ ¦ ¦ [ необ_элем ] ¦ В двойные квадратные скобки заключается не- ¦ ¦ ¦ обязательный синтаксический элемент. Напри- ¦ ¦ ¦ мер, синтаксис индексного оператора показан ¦ ¦ ¦ следующим образом: ¦ ¦ ¦ ¦ ¦ ¦ [ выраж_1 ] выраж_2 ¦ ¦ ¦ ¦ ¦ ¦ Это указывает на то, что "выраж_1" является ¦ ¦ ¦ необязательным, поскольку оно заключено в ¦ ¦ ¦ квадратные скобки. Однако выражение "выраж_2" ¦ ¦ ¦ является обязательным. ¦ ¦ ¦ ¦ ¦{выбор1¦выбор2}¦ Фигурные скобки и вертикальные разделители ¦ ¦ ¦ указывают на необходимость выбора между двумя ¦ ¦ ¦ или более элементами. Варианты выбора заклю- ¦ ¦ ¦ чаются в фигурные скобки и разделяются верти- ¦ ¦ ¦ кальной чертой. Вы должны выбрать один из ва- ¦ ¦ ¦ риантов. ¦ ¦ ¦ ¦ ¦ ¦ Например, необязательный параметр /W (уровень ¦ ¦ ¦ предупреждающих сообщений об ошибке) имеет ¦ ¦ ¦ следующий синтаксис: ¦ ¦ ¦ ¦ ¦ ¦ /W{0¦1¦2} ¦ ¦ ¦ ¦ ¦ ¦ Вы можете ввести /W0, /W1 или /W2, указав та- ¦ ¦ ¦ ким образом желаемый уровень предупреждений. ¦ ¦ ¦ Однако указывать /W3 не допускается, посколь- ¦ ¦ ¦ ку 3 не содержится ни в одном из вариантов ¦ ¦ ¦ выбора, которые указаны в фигурных скобках. ¦ ¦ ¦ ¦ ¦Повторяющиеся ¦ Три точки, следующие за элементами, показыва- ¦ ¦ элементы. ¦ ют, что можно в таком же виде ввести большее ¦ ¦ ¦ количество элементов. Ниже, например, приве- ¦ ¦ ¦ ден синтаксис директивы PUBLIC: ¦ ¦ ¦ ¦ ¦ ¦ PUBLIC имя[, имя ]. ¦ ¦ ¦ ¦ ¦ ¦ Точки за вторым элементом "имя" указывают, ¦ ¦ ¦ что вы можете ввести столько имен, сколько ¦ ¦ ¦ захотите, пока каждому из них будет предшест- ¦ ¦ ¦ вовать запятая. Однако, поскольку первое имя ¦ ¦ ¦ не заключено в квадратные скобки, вы должны ¦ ¦ ¦ ввести по крайней мере одно имя. ¦ ¦ ¦ ¦ ¦Определяемые ¦ В кавычки заключаются определяемые в тексте ¦ ¦ термины и ¦ термины. Например, термин "промежуточный", ¦ ¦ "подсказки" ¦ если он определяется в первый раз, заключает- ¦ ¦ ¦ ся в кавычки. ¦ L---------------+------------------------------------------------



Соглашения по вызовам, использующиеся в Паскале


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

Примечание: Более подробно соглашения о связях Паскаля рассматриваются в Главе 19.

Приведем пример функции на Ассемблере, в которой используют- ся соглашения Паскаля:

; ; Вызывается, как: TEST(i, j ,k) ; i equ 8 ; левый параметр j equ 6 k equ 4 ; правый параметр ; .MODEL SMALL .CODE PUBLIC TEST TEST PROC push bp mov bp,sp mov ax,[bp+i] ; получить i add ax,[bp+j] ; прибавить к i j sub ax,[bp+k] ; вычесть из суммы k pop bp ret 6 ; возврат, отбросить ; 6 байт параметров ; (очистка стека) TEST ENDP END

Заметим, что для очистки стека от передаваемых параметров используется инструкция RET 6.

На Рис. 18.5 показано состояние стека после выполнения инс- трукции MOV BP,SP:

. . . . . . ¦ ¦ +-----------------------+ SP -- ¦ BP вызывающей прогр. ¦ -- BP +-----------------------+ SP + 2 ¦ Адрес возврата ¦ BP + 2 +-----------------------+ SP + 4 ¦ k ¦ BP + 4 +-----------------------+ SP + 6 ¦ j ¦ BP + 6 +-----------------------+ SP + 8 ¦ i ¦ BP + 8 +-----------------------+ ¦ ¦ +-----------------------+ ¦ ¦ . . . . . .

Рис. 18.5 Состояние стека после инструкции MOV BP,SP

Соглашения по вызовам Паскаля требуют также, чтобы все внеш- ние и общедоступные идентификаторы указывались в верхнем регистре и без предшествующих подчеркиваний. Зачем может потребоваться ис- пользовать в программе на С++ соглашения по вызовам Паскаля? Программа, использующая соглашения Паскаля, занимает обычно нес- колько меньше места в памяти и работает быстрее, чем обычная программа на языке С++, так как для очистки стека от параметров не требуется выполнять n инструкций ADD SP.



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


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



Сохранение регистров


Большинство языков высокого уровня требуют, чтобы вызываемые процедуры сохраняли определенные регистры. Это можно сделать, за- нося их в стек в начале процедуры и извлекая из стека в конце процедуры.

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

USES элемент [,элемент] .

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

. . . myproc PROC PASCAL NEAR ARG @source:DWORD,@dest:DWORD,@count:WORD USES cx,si,di,foo MOV cx,@count MOV foo,@count LES di,@dest LDS si,@source REP MOVSB ENDP . . .

О сохранении регистров в языках Си и Паскаль можно подробнее узнать в Главе 18 и 19.

Оператор USES можно использовать только в тех процедурах, которые используют языковые соглашения, отличные от NOLANGUAGE.


При взаимодействии Турбо Ассемблера и Borland C++ вызываемые из программы на языке С++ функции Ассемблера могут делать все что угодно, но при этом они должны сохранять регистры BP, SP, CS, DS и SS. Хотя при выполнении функции Ассемблера эти регистры можно изменять, при возврате из вызываемой подпрограммы они должны иметь в точности такие значения, какие они имели при ее вызове.

Регистры AX, BX, CX, DX и ES, а также флаги могут произвольно из- меняться.

Регистры DI и SI представляют собой особый случай, так как в Borland C++ они используются для регистровых переменных. Если в модуле С++, из которого вызывается ваша функция на Ассемблере, использование регистровых переменных разрешено, то вы должны сох- ранить регистры SI и DI, если же нет, то сохранять их не нужно.

Однако неплохо всегда сохранять эти регистры, независимо от того, разрешено или запрещено использование регистровых перемен- ных. Трудно заранее гарантировать, что вам не придется компоно- вать данный модуль Ассемблера с другим модулем на языке С++, или перекомпилировать модуль С++ с разрешением использования регист- ровых переменных. При этом вы можете забыть, что изменения нужно также внести и в код Ассемблера.



Сообщения о фатальных ошибках -----------------------------------------------------------------


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



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


Для создание адресного выражения, которое указывает на теку- щий сегмент и счетчик инструкций, можно использовать операцию THIS. В режиме Ideal можно использовать следующий синтаксис:

THIS тип

Синтаксис режима Ideal позволяет вам строить адресное выра- жение на основе текущего сегмента и счетчика инструкций для за- данного типа.

В режиме MASM используется следующий синтаксис:

THIS выражение

Синтаксис режима MASM работает аналогично режиму Ideal, но для определения типа использует числовое значение выражения. Это следующие значения: 0=UNKNOWN, 1=BYTE, 2=WORD, 4=DWORD, 6=PWORD, 8=QQORD, 10=TBYTE, 0ffffh=NEAR, 0fffeh. Например:

ptr1 LABEL WORD ptr2 EQU THIS WORD ; аналогично ptr1



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


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

NNTYPE TYPEDEF PTR BYTE

Тогда оператор:

NNTEST NTTYPE ?

создает экземпляр именованного типа NTYPE (определяя переменную NTTEST). В данном примере, поскольку задано значение неинициали- зируемых данных ?, начальные данные в текущем сегменте не генери- руются.

Способ инициализации экземпляра именованного типа зависит от типа, который этот именованный тип представляет. Например, NTTYPE в предыдущем примере - это слово, поэтому он будет инициализиро- ваться, как если бы вы следующим образом использовали директиву DW:

NTTYPE 1,2,3 ; представляет указатель значений 1,2,3 DW 1,2,3 ; то же, что NTTYPE 1,2,3

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

foo STRUC f1 DB ? ENDS bar TYPEDEF foo bar {f1=1} ; должен быть инициализатор структуры



Создание экземпляра данных перечислимого типа


Экземпляр данных перечислимого типа вы можете создать, ис- пользую как директиву определения данных имя перечислимого типа данных. Предположим, например, что вы определили следующее:

ETYPE ENUM FEE,FIE,FOO,FUM

Тогда оператор:

ETEST ETYPE ?

будет создавать экземпляр перечислимого типа данных ETYPE (опре- деляя переменную ETEST). В данном примере, поскольку задано зна- чение неинициализируемых данных ?, начальные данные в текущем сегменте не генерируются.

Экземпляры перечислимого типа данных всегда представляют со- бой байт, слово или двойное слов, в зависимости от максимального значения, представленного в данных перечислимого типа.



Создание экземпляра объекта


Чтобы создать экземпляр объекта, вы можете вызвать метод конструктора объекта (который выделяет память для экземпляра объ- екта), или распределить экземпляр объекта в предопределенном сег- менте данных.

Вы можете создать экземпляр объекта точно также, как вы соз- даете экземпляр структуры. Рассмотрите, например, следующие эк- земпляры объектов:

foolist list () ; экземпляр списка fooqueue label queue queue () ; экземпляр очереди queue (list_head=mynode,list_tail=mynode) ; экземпляр очереди

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


Создание экземпляра объекта в инициализированном или неини- циализированном сегменте данных полностью совпадает с созданием экземпляра структуры. Фактически, объекты в Турбо Ассемблере представляют собой структуры с некоторыми расширениями. Одним из таких расширений является элемент структуры @Mptr_<имя_объекта>.

Объектный тип данных с виртуальными методами - это структу- ра, содержащая один элемент, указывающий на таблицу указателей виртуальных методов. Именем данного элемента является @Mptr_<имя_ объекта>. Обычно инициализируется с помощью конструктора метода. Однако, вы можете построить статические объекты, не имеющие конс- труктора, но инициализируемые с помощью инициализатора в сегменте данных.

Если вы указываете используемое по умолчанию значение эле- мента @Mptr_<имя_объекта>, то Турбо Ассемблер будет корректно инициализировать экземпляр объекта.

Другим отличием структур и объектов является то, что объекты могут наследовать элементы из предыдущих определений объекта. При подобном наследовании Турбо Ассемблер интерпретирует его, как вложенную структуру. В связи с этим для инициализации объектных данных не рекомендуется использовать угловые скобки (<>).



Создание экземпляра структуры или объединения


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

ASTRUC STRUC B DB "xyz" C DW 1 D DD 2 ASTRUC ENDS

BUNION UNION X DW ? Y DD ? Z DB ? BUNION ENDS

Тогда операторы:

ATEST ASTRUC ? BTEST BUNUION ?

будут создавать экземпляры структуры astruc (определяя переменную atest) и объединения bunion (определяя переменную btest). Пос- кольку в примере указывается значение неинициализированных данных ?, то начальные данные в текущий сегмент не вводятся.



Создание экземпляра таблицы


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

TTYPE TABLE VIRTUAL MoveProc:WORD=MoveRtn, \продолжение. VIRTUAL MsgProc:DWORD=MsgRtn, \продолжение. VIRTUAL DoneProc:WORD=DoneRtn,

Тогда оператор:

TTEST TTYPE ?

создает экземпляр таблицы TTYPE (определяя переменную TTEST). В данном примере, поскольку задано значение неинициализируемых дан- ных ?, начальные данные в текущем сегменте не генерируются.



Создание экземпляра таблицы виртуальных методов объекта


Для каждого объекта, содержащего виртуальные методы, необхо- димо наличие доступной таблицы виртуальных методов. Правильное размещение данной таблицы определяет множество факторов, включая то, какую программную модель вы используете, хотите вы получить таблицу NEAR или FAR и т.д. Турбо Ассемблер требует от вас только размещения данной таблицы. Экземпляр последних определенных объ- ектов вы можете создать, используя псевдооперацию TBLINST, кото- рая имеет следующий синтаксис:

TBLINST

TBLINST определяет в качестве адреса таблицы виртуальных ме- тодов объекта @TableAddr_<имя_объекта>. Это эквивалентно следую- щему:

@TableAddr_<имя_объекта> @TableAddr_<имя_объекта> {}

Назад | Содержание | Вперед



Создание экземпляра записи


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

MYREC RECORD VEL:3=4,MODE:2,SIZE:4=15

Тогда оператор:

MTEST MYREC ?

будет создавать экземпляр записи myrec (определяя переменную mtest). В данном примере данные в сегмент не помещаются, так как задаются неинициализированные данные (?).

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



Ссылки на структуры, объединения и смещения элементов в таблице


Элементы структур, объединений и таблиц являются глобальными переменными, значения которых представляют собой в режиме MASM смещения в структуре, объединении или таблице. Однако в режиме Ideal элементы этих типов данных рассматриваются, как локальные по отношению к типам данных. Операция точки (.) позволяет вам по- лучить смещения элементов. В режиме Ideal используется следующий синтаксис:

выражение.идентификатор

где "выражение" представляет адрес структуры, объединения или эк- земпляра таблицы. "Идентификатор" должен быть элементом структу- ры, объединения или таблицы. Операция точки возвращает смещение элемента в структуре.

В режиме MASM также имеется версия операции точки. Однако ее функция аналогична операции + и имеет следующий синтаксис:

выражение_1 + выражение_2



Стандартные значения идентификаторов


Некоторые идентификаторы всегда представляют конкретные зна- чения, и для того, чтобы их использовать, определения не требует- ся. Эти идентификаторы и их значения перечислены в следующей таблице:

Стандартные идентификаторы Таблица 5.9 ------------------------T---------------------------------------¬ ¦ Идентификатор ¦ Значение ¦ +-----------------------+---------------------------------------+ ¦ $ ¦ Значение текущего счетчика адре- ¦ ¦ ¦ са программы. ¦ ¦ NOTHING ¦ 0 ¦ ¦ ? ¦ 0 ¦ ¦ UNKNOWN ¦ 0 ¦ ¦ BYTE ¦ 1 ¦ ¦ WORD ¦ 2 ¦ ¦ DWORD ¦ 4 ¦ ¦ PWORD ¦ 6 ¦ ¦ FWORD ¦ 6 ¦ ¦ QWORD ¦ 8 ¦ ¦ TBYTE ¦ 10 ¦ ¦ ¦ ¦ ¦ NEAR ¦ 0ffffh ¦ ¦ FAR ¦ 0fffeh ¦ ¦ PROC ¦ 0ffffh или 0fffeh, в зависимос- ¦ ¦ ¦ ти от текущей модели. ¦ ¦ ¦ ¦ ¦ CODEPTR ¦ 2 или 4, в зависимости от теку- ¦ ¦ ¦ щей модели. ¦ ¦ DATAPTR ¦ 2 или 4, в зависимости от теку- ¦ ¦ ¦ щей модели. ¦ L-----------------------+----------------------------------------



Старшинство ключевых слов


Важно понимать, как Турбо Ассемблер распознает строки исход- ного текста. Это позволит вам избежать записи кода, который может привести к непредсказуемым результатам. Рассмотрим, например, следующий фрагмент программы:

NAME SEGMENT

Если вы записали эту строку, рассчитывая открыть сегмент с именем NAME, то будете разочарованы. Турбо Ассемблер распознает ключевое слово NAME раньше, чем SEGMENT, называя ваш код именем SEGMENT.

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



Старшинство операций в режиме Ideal


В режиме Ideal применяются следующие правила старшинства операций:

1. Наивысший приоритет (приоритет 1) имеют все ключевые сло- ва в первой позиции строки. Они проверяются первыми.

2. Ключевые слова на второй позиции строки имеют второй при- орите и рассматриваются во вторую очередь.



Старшинство операций в режиме MASM


Правила синтаксического анализа строк в режиме MASM значи- тельно более сложны, чем в режиме Ideal. Вместо двух здесь имеет- ся три уровня приоритета:

1. Наивысший приоритет (приоритет 1) присваивается отдельным ключевым словам в первой позиции (таким как NAME и %OUT).

2. Следующий по старшинству приоритет (приоритет 2) имеют все идентификаторы, обнаруженные на второй позиции.

3. Все другие ключевые слова в первой позиции имеют наимень- ший (третий) приоритет.

Примечание: Внутри определений структур Турбо Ассемб- лер интерпретирует ключевые слова приоритета 1 как приори- тет 3. В этом случае ключевые слова приоритета 2 имеют выс- ший приоритет.

Например, в следующем фрагменте кода:

NAME SEGMENT

NAME - это ключевое слово первого приоритета, а SEGMENT - ключевое слово второго приоритета. Таким образом, Турбо Ассемблер будет интерпретировать эту строку, как директиву NAME, а не как директиву SEGMENT. В другом примере:

MOV INSTR,1

MOV - ключевое слово приоритета 3, а INSTR - ключевое слово прио- ритета 2. Таким образом, Турбо Ассемблер интерпретирует эту стро- ку как инструкцию INSTR, а не как инструкцию MOV (как вы можете предполагать).



Старые директивы определения сегментов и Borland C++


Коснемся теперь проблемы организации интерфейса Турбо Ассем- блера с кодом языка С++, где используются директивы определения сегментов старого типа (стандартные директивы определения сегмен- тов). Например, если вы замените в модуле DOTOTAL.ASM упрощенные директивы определения сегментов директивами старого типа, то по- лучите следующее:

DGROUP group _DATA,_BSS _DATA segment word public 'DATA' EXTRN _Repetitions:WORD ; внешний идентификатор PUBLIC _StartingValue ; доступен для других модулей _StartValue DW 0 _DATA ends _BSS segment word public 'BSS' RunningTotal DW ? _BSS ends _TEXT segment byte public 'CODE' assume cs:_TEXT.ds:DGROUP,ss:DGROUP PUBLIC _DoTotal _DoTotal PROC ; функция (в малой модели памяти ; вызывается с помощью вызова ; ближнего типа) mov cx,[_Repetitions] ; счетчик выполнения mov ax,[_StartValue] mov [RunningTotal],ax ; задать начальное ; значение TotalLoop: inc [RunningTotal] ; RunningTotal++ loop TotalLoop mov ax,[RunningTotal] ; возвратить конечное ; значение (результат) ret _DoTotal ENDP _TEXT ENDS END

Данная версия директив определения сегментов не только длин- нее, то также и хуже читается. К тому же при использовании в программе на языке С++ различных моделей памяти ее труднее изме- нять. При организации интерфейса с Borland C++ в общем случае в использовании старых директив определения сегментов нет никаких преимуществ. Если же вы тем не менее захотите использовать при организации интерфейса с Borland C++ старые директивы определения сегментов, вам придется идентифицировать корректные сегменты, со- ответствующие используемым в коде на языке С++ моделям памяти.

Простейший способ определения, какие сегментные директивы старых версий должны выбираться для компоновки с той или иной программой Borland С++, заключается в компиляции главного модуля программы на Borland С++ для желаемой модели памяти с параметром -S, что тем самым заставит Borland С++ сгенерировать ассемблерную версию соответствующей программы на Borland С++. В этой версии кодов Си вы сможете найти все старые сегментные директивы, ис- пользуемые Турбо Cи; просто скопируйте их в вашу ассемблерную часть программы.

Вы также можете посмотреть, как будут выглядеть соответству- ющие старые директивы, скомпилировав их обычным образом (без па- раметра -S) и использовав TDUMP - утилиту, поставляемую Турбо Ас- семблером, чтобы получить все записи определения сегмента. Ис- пользуйте следующую командную строку:

tdump -OI segdef module.obj



String too long


(Слишком длинная строка)

Указанная в кавычках строка имеет длину, превышающую макси- мально допустимую - 255 символов.



Строки


Строковые параметры, независимо от размера, обычно никогда не заносятся в стек. Вместо этого Турбо Паскаль заносит в стек указатель (дальнего типа) на строку. Вызываемая подпрограмма не должна изменять строку, на которую ссылается указатель. Если это необходимо, подпрограмма может создать и работать с копией стро- ки.

Единственное исключение из этого правила - это случай, когда подпрограмма в перекрываемом (оверлейном) модуле A передает как параметр-значение строковую константу подпрограмме в перекрывае- мом модуле B. В этом контексте перекрываемый модуль означает лю- бой модуль, скомпилированный с директивой {$O+} (допускаются оверлеи). В этом случае перед тем, как будет сделан вызов и адрес стека будет передан программе в модуле B, в стеке для строковой константы резервируется временная память.



Строковые константы


Строковые константы всегда начинаются с одиночной или двой- ной кавычки и завершаются соответствующей кавычкой. Турбо Ассемб- лер преобразует заключенные в кавычки символы в значения ASCII.

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

'It''s represent' It's



Symbol already defined:__


(Имя идентификатора уже определено)

Указанное имя идентификатора уже было ранее объявлено с тем же самым типом. Например:

BB DB 1,2,3 BB DB ? ; ошибка: BB уже определено



Symbol already different kind


(Имя идентификатора уже объявлено с другим типом)

Указанное идентификатора имя было ранее объявлено с другим типом, например:

BB DB 1,2,3 BB DW ? ; ошибка: BB уже объявлено с типом BYTE



Symbol has no width or mask


(Имя идентификатора не может быть использовано в операциях WIDTH и MASK)

Операнд операции WIDTH или MASK не является именем записи или именем поля записи. Например:

B DB 0 mov ax,MASK B ; В не является полем записи