(Отсутствует правая угловая скобка)
Выражение, используемое для инициализации структуры, объеди- нения или записи, не оканчивается правой угловой скобкой (>) - парной для левой угловой скобки, указывающей начало списка на- чальных значений. Например:
MYSTRUC STRUCNAME <1,2,3
(Требуется правая фигурная скобка)
Эта ошибка возникает в структуре, таблице или записи, когда ожидается }, но она не найдена.
(Отсутствует правая круглая скобка)
Выражение содержит левую круглую скобку без парной ей правой угловой скобки. Например:
Х = 5 * (4 + 3
В выражениях обязательно должно соблюдаться соответствие ле- вых и правых круглых скобок.
(Отсутствует правая квадратная скобка)
Выражение, представляющее собой ссылку на память, указано без правой квадратной скобки, которая должна соответствовать отк- рывающей левой квадратной скобке, обозначающей начало выражения. Например:
mov ax,[SI ; ошибка: нет закрывающей ; скобки (]) после SI
В выражениях обязательно должно соблюдаться соответствие ле- вых и правых квадратных скобок.
(Не указан стековый параметр)
В команде арифметики с плавающей запятой не указан второй операнд, т.е. операнд, указываемый после запятой. Например:
FADD ST,
(Не указано имя поля структуры)
В режиме Ideal после селектора поля структуры (т.е. после точки) не указано имя поля этой структуры. Например:
Ideal STRUC DEMO DB ? ENDS COUNT DW 0 mov,[(DEMO bx).]
При обращении к полю структуры справа от точки обязательно должно быть указано поле той структуры, имя которой стоит слева от точки.
Непосредственная макродиректива % интерпретирует строку текста так, как если бы это было тело макрокоманды. Приведем ее синтаксис:
# строка_тела_макрокоманды
где "строка_тела_макрокоманды" представляет тело макрокоманды, используемое для непосредственного макрорасширения, например:
SEGSIZE EQU <TINY>
LANGUAGE EQU <WINDOWS PASCAL>
% MODEL SEGSIZE,LANGUAGE ; дает MODEL TINY,WINDOWS PASCAL
(Использование имени группы или сегмента недопустимо)
Указано имя группы или сегмента там, где это недопустимо. Например:
CODE SEGMENT rol ax,CODE ; ошибка: здесь нельзя указывать ; имя сегмента
Описание Турбо Ассемблера поставляется в виде двух пособий: "Руководства пользователя по Турбо Ассемблеру" (данный текст) и "Краткого справочного руководства по Турбо Ассемблеру". В "Руко- водстве пользователя" даются основные инструкции по использованию Турбо Ассемблера, организации его интерфейса с другими языками и описываются предопределенные операции, идентификаторы и директи- вы, используемые Турбо Ассемблером. "Краткое справочное руководс- тво" позволяет быстро находить информацию по инструкциям процес- сора и сопроцессора и директивам.
Рассмотрим содержание "Руководства пользователя" более под- робно.
Турбо Паскаль ожидает, что перед возвратом управления из подпрограммы все параметры в стеке центрального процессора будут удалены.
Есть два способа настройки стека. Вы можете использовать ин- струкцию RET N (где N - это число байт передаваемых, то есть за- несенных в стек, параметров), либо сохранить адрес возврата в ре- гистрах (или в памяти) и извлечь параметры из стека поочередно. Такую технику извлечения полезно использовать для оптимизации по скорости при работе с процессором 8086 или 8088 (самые "медлен- ные" процессоры серии), когда на адресацию типа "база плюс смеще- ние" затрачивается минимум 8 циклов за обращение. Это позволяет также сэкономить место, так как инструкция POP занимает только один байт.
Примечание: Если вы используете директивы .MODEL, PROC и ARG, то Ассемблер автоматически добавляет во все инструк- ции RET число байт извлекаемых параметров.
Если вы не задаете для них имена с предшествующий префиксом локального идентификатора, все аргументы, заданные в заголовке процедуры, определены ли они с помощью директивы ARG (передавае- мые аргументы), RETURN (возвращаемые аргументы) или LOCAL (ло- кальные переменные) имеют глобальную область действия.
Идентификаторы с локальной областью действия разрешает дирек- тива LOCALS. Например:
. . . LOCALS test1 PROC PASCAL FAR ARG @a:WORD,@d:WORD,@c:BYTE LOCAL @x:WORD,@y:DWORD MOV ax,@a MOV @x,ax LES di,@b MOV WORD ptr @y,di MOV WORD ptr @y+2,es MOV @c,'a' RET ENDP
test2 PROC PASCAL FAR ARG @a:DWORD,@b:BYTE LOCAL @x:WORD LES di,@a MOV ax,es:[di] MOV @x,ax CMP a1,@b jz @dn MVO @x,0 @dn: MOV ax,@x RET ENDP . . .
Примечание: Об управлении областью действия идентифи- каторов подробнее рассказывается в Главе 11.
Поскольку в данном примере используются переменные локальной области действия, их имена существуют только в теле процедуры. Таким образом, в test2 можно снова использовать имена @a, @b и @x.
Ограничение области действия границами блока позволяет иден- тификатору иметь область действия, соответствующую процедуре или функции. Турбо Ассемблер поддерживает два типа области действия в границах блока: в стиле MASM и в стиле самого Турбо Ассемблера.
В MASM версий 5.1 и 5.2 метки NEAR, определенные с помощью директивы двоеточия (:), если они находятся в процедуре, и если вы выбрали в директиве MODEL соглашения языка, имеют область действия в границах блока. Однако эти идентификаторы не являются полными идентификаторами, имеющими область действия в границах блока: нигде в программе их нельзя определить никак иначе, чем как ближнюю метку. Например:
varsion m510 model small,c
codeseg
foo proc a: jmp a ; относится к процедуре FOO foo endp
bar proc a: jmp a ; относится к процедуре BAR bar endp
a = 1 ; недопустимо!
В Турбо Ассемблере имеется несколько директив, которые помо- гают вам работать с модулями кода. Эти директивы описываются в оставшейся части данной главы.
Программы Турбо Ассемблера могут вызывать функции С++ и ссы- латься на внешние переменные Си. Программы Borland C++ аналогич- ным образом могут вызывать общедоступные (PUBLIC) функции Турбо Ассемблера и обращаться к переменным Турбо Ассемблера. После то- го, как в Турбо Ассемблере устанавливаются совместимые с Borland C++ сегменты (как описано в предыдущих разделах), чтобы совместно использовать функции и переменные Borland C++ и Турбо Ассемблера, нужно соблюдать несколько простых правил.
Общие арифметические операции используются для работы с константами, значениями идентификаторов и значениями других общих арифметических операций. Общими операциями являются операции сло- жения, вычитания, умножения и деления. Другие операции специально предназначены для программирования на языке Ассемблера. В следую- щих разделах мы обсудим все эти темы.
Имеется ряд директив, которые позволяют вам управлять видом файла листинга. Общие директивы управления листингом включают в себя следующие директивы:
- .LIST ; только режим MASM
- .XLEST ; только режим MASM
- %LIST
- %NOLIST
- %CTLS
- %NOCTLS
- %SYMS
- %NOSYMS
Директива %LIST выводит в файл листинга все строки исходного кода. Эта директива назначается по умолчанию при создании файла листинга. Чтобы запретить вывод в листинг всех строк исходного кода, используйте директиву %NOLIST. Приведем пример:
%NOLIST ; запретить листинг INCLUDE MORE .INC %LIST ; разрешить листинг
Директивы .LIST и .XLIST работают также, как директивы %LIST и %NOLIST. Приведем пример:
.LIST jmp xyz ; эта строка всегда выводится .XLIST add dx,ByteVar ; не содержится в листинге
Для управления включением в листинг директив управления лис- тингом вы можете использовать директивы %CTL и %NOCTL. Директива %CTLS приводит к включению в листинг директив управления листин- гом (таких как %LIST, %INCL и т.д.). Обычно они в листинг не вы- водятся. Эта директива действует для всех последующих строк, поэ- тому сама директива %CTLS в листинг не выводится. Директива %NOCTLS изменяет действие директивы %CTLS на обратное. После за- дания директивы %NOCTLS все последующие директивы управления лис- тингом в листинг включаться не будут (этот режим используется Турбо Ассемблером по умолчанию, когда начинается ассемблирование исходного файла). Например:
%CTLS %NOLIST ; эта директива не будет включена в ; исходный файл %NOCTLS %LIST ; это не будет включаться в листинг
Для задания включения или не включения в файл листинга таб- лицы идентификаторов вы можете использовать директивы %SYMS и %NOSYMS (по умолчанию таблица идентификаторов выводится). Таблица будет выводиться в конце файла листинга.
Приведем синтаксис директивы %SYMS:
%SYMS
Директива %NOSYMS имеет следующий синтаксис:
%NOSYMS
Турбо Ассемблер связывает тело макрокоманды, состоящей из нескольких строк (включая директивы, инструкции и другие макроко- манды) с символьным именем макрокоманды. При использовании имени макрокоманды в качестве директивы Турбо Ассемблер включает в программу тело макрокоманды (операторы). Таким образом вы можете использовать макрокоманду, состоящую из нескольких строк, нес- колько раз.
Приведем синтаксис общей макрокоманды из нескольких строк режима Ideal:
MACRO имя список_параметров тело_макрокоманды ENDM
В режиме MASM общее определение макрокоманды из нескольких строк имеет следующий синтаксис:
имя MACRO список_параметров тело_макрокоманды ENDM
где "имя" - это имя определяемой вами макрокоманды из нескольких строк. "Тело_макрокоманды" содержит операторы, которые составляет тело макрорасширения. В макрокоманду вы можете помещать любые до- пустимые операторы Турбо Ассемблера (и любое их число). Макроко- манду завершает ключевое слово ENDM.
В следующем примере определяется макрокоманда с именем PUSHALL, которая при вызове включает в вашу программу тело макро- команды, состоящее из трех инструкций PUSH:
PUSHALL MACRO PUSH AX BX CX DX PUSH DS SI PUSH ES DI ENDM
"Список_параметров" - это список формальных аргументов (их идентификаторов) макрокоманды. Он имеет следующий синтаксис:
[формальный_аргумент [,формальный_аргумент].]
В макрокоманде вы можете использовать любое число формальных аргументов (если они не умещаются на одной строке, для продолже- ния на другой строке можно использовать символ /). Например:
ADDUP MACRO dest,\ ; dest - это первый формальный ; аргумент, а s1,s2 - ; это второй и третий ; аргумент макрокоманды MOV dest,s1 ADD dest,s2 ENDM
Каждый формальный аргумент имеет следующий синтаксис:
имя_формального_аргумента[:тип_аргумента]
где "имя_аргумента" - это символьное имя, используемое для подс- тановки (вместо него) фактического параметра, передаваемого мак- рокоманд при ее вызове. Необязательный "тип_аргумента" задает ка- кую-то информацию о виде фактического параметра, воспринимаемого при вызове макрокоманды. Поддерживаются следующие типы:
В стандартном режиме Турбо Ассемблер выполняет ассемблирова- ние за один проход, а MASM - за два прохода. Поэтому Турбо Ас- семблер - более быстрый ассемблер по сравнению с MASM. Однако од- нопроходность Турбо Ассемблера может привести к некоторой его несовместимости с MASM при разрешении ссылок вперед и обработке конструкций, зависящих от прохода. В TASM имеется параметр ко- мандной строки (/m), с помощью которого можно задать число прохо- дов. Если требуется обеспечить максимум совместимости с MASM, то нужно указать двухпроходный режим (/m2). (См. Главу 2, где данный параметр обсуждается более подробно.)
Использование данного параметра командной строки будет гене- рировать режим, совместимый с MASM (два прохода), когда присутс- твуют следующие конструкции:
- директивы IF1 и IF2;
- директивы ERR1 и ERR2;
- ссылки вперед с IFDEF и IFNDEF;
- опережающие ссылки с операцией .TYPE;
- рекурсивно определенные числа, такие, как
NMBR=NMBR+1;
- рекурсивно определенные текстовые макрокоманды или тексто- вые макрокоманды, на которые имеются опережающие ссылки, такие, как:
LNAME CATSTR LNAME,<1>
- макрокоманды, на которые имеются опережающие ссылки.
Синтаксис уточненного идентификатора Турбо Паскаля, при ко- тором для доступа к объекту в заданном модуле используется имя модуля и точка, несовместим с синтаксическими правилами Турбо Ас- семблера и будет, таким образом, отвергнут. Описание:
EXTRN SYSTEM.Assing : FAR
приведет к тому, что Турбо Ассемблер выдаст сообщение об ошибке.
Имеется также два других ограничения на использование в Тур- бо Паскале объектов EXTRN. Первое из них состоит в том, что в ссылках на процедуру или функцию не могут выполняться арифмети- ческие операции с адресами. Таким образом, если вы объявите:
EXTRN PublicProc : FAR
то не сможете записать оператор вида:
call PublicProc + 42
Второе ограничение относится к тому, что компоновщик Турбо Паскаля не будет распознавать операции, которые разделяют слова на байты, поэтому вы не можете применять такие операции к объек- там EXTRN. Например, если вы объявите:
EXTRN i : WORD
то не сможете использовать в модуле Турбо Ассемблера выражения LOW i или HIGH i.
(При расширении объединения допускается указывать только од- но поле непустым)
При инициализации объединения, определенного директивой UNION, указано более одного значения. Например:
U UNION DW ? DD ? ENDS UINST U <1,2> ;ошибка: можно указать <?,2> либо <1,?>
В объединении можно инициализировать только одно поле.
(Допускается только одна директива генерации кода инициали- зации)
Это сообщение выдается, если в модуле указано более одной директивы .STARTUP или STARTUPCODE.
(Открытый условный блок)
Обнаружена директива END, означающая конец исходного файла. Однако условно ассемблируемый блок, открытый одной из директив вида IFxxx, не был закрыт директивой ENDIF. Например:
IF BIGBUF END ; нет директивы ENDIF перед директивой END
Эта ошибка обычно выдается, если вместо директивы окончания условного блока ENDIF, ошибочно указана директива END.
(Открытая процедура)
Обнаружена директива END, означающая конец исходного файла. Однако блок описания процедуры, открытый директивой PROC, не был закрыт директивой ENDР. Например:
MYFUNC PROC END ; нет директивы ENDР перед директивой END
Эта ошибка обычно выдается, если вместо директивы конца бло- ка процедуры - ENDP ошибочно указана директива END.
(Открытый сегмент)
Обнаружена директива END, означающая конец исходного файла. Однако сегмент, открытый директивой SEGMENT, не был закрыт дирек- тивой ENDS. Например:
DATA SEGMENT END ; нет директивы ENDS перед директивой END
Эта ошибка обычно выдается, если вместо директивы конца сег- мента - ENDS ошибочно указана директива END.
(Не указан конец определения структуры)
Обнаружена директива END, означающая конец исходного файла. Однако определение структуры, начало которой указано директивой STRUCTURE, не было завершено директивой ENDS. Например:
X STRUC VAL1 DW ? END ; нет директивы ENDS перед директивой END
Эта ошибка обычно выводится, если вместо директивы конца структуры ENDS ошибочно указана директива END.
Изменения, внесенные в операции выражений в режиме Ideal, позволяют повысить мощность и гибкость некоторых операций, оста- вив без изменения общее поведение выражений. Для того, чтобы со- действовать некоторым комбинациям операций, изменен порядок стар- шинства некоторых операций.
Аккуратно задавайте точку (.) в элементах структуры, на ко- торые вы ссылаетесь. Операция точки для элементов структур в ре- жиме Ideal является более строгой. Выражение слева от точки долж- но представлять собой имя элемента структуры. Приведем примеры загрузки регистров значениями конкретных элементов структур:
; Опишем переменные с помощью структурных типов S_Stuff SomeStuff <> O_Stuff OtherStuff <> mov ax,[S_Stuff.Amount] ; загрузить значение размером в ; слово mov bl,[O_Stuff.Amount] ; загрузить значение размером в ; байт
Операции сравнения позволяют сравнить два выражение и прове- рить их равенство или неравенство или что одно из них больше или меньше другого. Эти операции равны -1, если условие истинно (True), или 0 в противном случае. Следующая таблица показывает, как можно использовать эти операции.
Операции сравнения Таблица 5.18 ------------------------------T---------------------------------¬ ¦ Выражение ¦ Значение ¦ +-----------------------------+---------------------------------+ ¦ выражение_1 EQ выражение_2 ¦ -1, если выражение_1 равно вы- ¦ ¦ ¦ ражению_2, в противном случае ¦ ¦ ¦ 0. ¦ ¦ выражение_1 NE выражение_2 ¦ -1, если выражение_ 1 не равно ¦ ¦ ¦ выражению_2, в противном случае ¦ ¦ ¦ 0. ¦ ¦ ¦ ¦ ¦ выражение_1 GT выражение_2 ¦ -1, если выражение_1 больше вы- ¦ ¦ ¦ ражения_2, в противном случае ¦ ¦ ¦ 0. ¦ ¦ ¦ ¦ ¦ выражение_1 GE выражение_2 ¦ -1, если выражение_1 больше или ¦ ¦ ¦ равно выражению_2, в противном ¦ ¦ ¦ случае 0. ¦ ¦ ¦ ¦ ¦ выражение_1 LT выражение_2 ¦ -1, если выражение_1 меньше вы- ¦ ¦ ¦ ражения_2, в противном случае ¦ ¦ ¦ 0. ¦ ¦ ¦ ¦ ¦ выражение_1 LE выражение_2 ¦ -1, если выражение_1 меньше или ¦ ¦ ¦ равно выражения_2, в противном ¦ ¦ ¦ случае 0. ¦ L-----------------------------+----------------------------------
Операции EQ или NE интерпретируют выражения, как числа без знака. Например, -1 EQ 0ffffh имеет значение -1 (если только вы не выбрали процессор 80386 или не используете режим Ideal; в пос- леднем случае значение -1 имеет выражение -1 EQ 0ffffffffh).
Операции Gt, GE, LT и LE интерпретируют выражения, как числа со знаком. Например, 1 GE -1 имеет значение -1, но 1 GE 0ffffh имеет значение 0.
Операция : определяет ближнюю метку кода и имеет синтаксис:
имя:
где "имя" - это идентификатор, который вы не объявляли ранее в исходном коде. Ближние метки кода вы можете размещать на строке кода, где содержится только одна метка, или в начале строки перед инструкцией. Обычно ближние метки кода используются в качестве адреса перехода в инструкциях JMP и CALL в том же сегменте.
Если вы не используете директиву PUBLIC, чтобы метка была доступна из других файлов, она доступна только в текущем исходном файле.
Данная директива работает точно также, как при использовании директивы LABEL для определения ближней метки (NEAR). Например, A: эквивалентно A LABEL NEAR.
Приведем пример использования операции :
jne A ; пропустить следующую инструкцию inc si A: ; jne передает управление сюда
В инструкции BOUND требуется указывать операнд типа WORD (слово), а не DWORD (двойное слово). Это позволяет вам определить в нижнюю и верхнюю границу в виде двух констант размером в слово, что устраняет необходимость преобразования операнда в DWORD явным образом (с помощью DWORD PTR). В режиме MASM вы должны записы- вать:
BOUNDS DW 1,4 ; нижняя и верхняя границы BOUND DWORD PTR BOUNDS ; требуется в режиме MASM
Однако в режиме Ideal требуется только записать:
BOUNDS DW 1,4 ; нижняя и верхняя границы BOUND [BOUNDS] ; допускается в ; режиме Ideal
(Не совпадают типы операндов)
Тип одного из операндов команды не совпадает с типом другого операнда либо не является допустимым для данной команды. Напри- мер:
ABC DB 5 . mov ax,ABC
(Для статического элемента таблицы операция не допускается)
Для получения адреса статического элемента таблицы использо- вана операция точки. Это не допускается.
Оперативную справочную информацию по Турбо Ассемблеру можно получить с помощью резидентной с памяти справочной программы-ути- литы TASMHELP. Например, если вы используете Турбо отладчик, мож- но загрузить TASMHELP, затем загрузить Турбо отладчик и получать во время отладки справочную информацию по Турбо Ассемблеру. Либо вы можете использовать TASMHELP для поиска информации по языку при написании исходного кода.
Так как TASMHELP - это резидентная в памяти программа, ее нужно загрузить в память перед запуском редактора. Чтобы загру- зить TASMHELP, в ответ на подсказку командной строки DOS C: набе- рите:
TASMHELP
После этого вы можете запустить редактор, как обычно. Нахо- дясь в редакторе, нажмите для вызова TASMHELP оперативную клавишу - клавишу 5 на дополнительной клавиатуре (справа). Если при этом курсор у вас находится на пустой строке, TASMHELP выведет таблицу справочных экранов. Для перемещения курсора по ключевым словам используйте клавишу Tab и нажмите клавишу Enter.
Программа TASMHELP работает точно также, как справочная ути- лита компилятора THELP.
Подробнее о TASMHELP и ее параметрах командной строки можно узнать в документации по компилятору, где описывается работа с THELP.
Глобальные идентификаторы действуют также, как общедоступ- ные, при этом вам не нужно определять PUBLIC или EXTRN. Если пе- ременная определена в модуле, она действует как общедоступная. Если нет, она действует как внешняя. Для определения глобальных идентификаторов вы можете использовать директиву GLOBAL. Директи- ва GLOBAL имеет тот же синтаксис, что директивы PUBLIC и EXTRN (их синтаксис описан в предыдущих разделах).
Директива GLOBAL позволяет вам иметь включаемый файл, кото- рый будет включаться во все исходные модули. Включаемый файл со- держит все совместно используемые данные, определенные как гло- бальные идентификаторы. Когда вы в каждом модуле ссылаетесь на эти элементы данных, директива GLOBAL действует как директива EXTRN, описывая для другого модуля, как определены данные.
Перед тем, как использовать его где-либо в исходном коде, вы должны описать идентификатор как GLOBAL. Кроме того заметим, что при задании аргументов директивы GLOBAL используется тот же син- таксис, что и в директиве EXTRN.
Приведем пример:
GLOBAL X:WORD, Y:BYTE X DW 0 ; идентификатор стал доступен в ; других модулях mov al, Y ; описан как внешний
Вы можете использовать идентификаторы, как динамические точ- ки входа для динамически компонуемых библиотек (DLL). Для описа- ния идентификаторов, которые будут доступны таким образом, ис- пользуйте директиву PUBLICDDL. Она имеет следующий синтаксис:
PUBLICDLL [язык] идентификатор [,[язык] идентификатор] .
Турбо Ассемблер описывает идентификатор в объектном файле, как динамически компонуемую точку входа, благодаря чему они могут быть доступны в других программах. Спецификатор "язык" приводит к применению специфических для языка соглашений к конкретному имени идентификатора. Допустимыми спецификаторами языка являются специ- фикаторы C, CPP, PASCAL, BASIC, FORTRAN, PROLOG и NOLANGUAGE.
Приведем пример кода с использованием PUBLICDLL:
PUBLICDLL XYPROC ; процедура XYPROC доступна как XYPOROXC PROC NEAR ; динамически компонуемая точка ; входа
Вы можете легко определить процедуры, которые используют в Турбо Ассемблере соглашения по интерфейсу языков высокого уровня. Соглашения по интерфейсу поддерживаются для языков NOLANGUAGE (Ассемблер), BASIC, PROLOG, FORTRAN, C, CPP (C++) и PASCAL.
Турбо Ассемблер выполняет всю работу по генерации корректно- го кода начала (вход в процедуру) и завершения (выход из процеду- ры), необходимых для соблюдения соглашений заданного языка.
С помощью директивы MODEL вы можете задать язык, используе- мый по умолчанию. Подробности можно найти в Главе 7. Если используемый по умолчанию язык задан, все процедуры, для которых не задается другой язык, используют соглашения назначенного по умолчанию языка.
Чтобы переопределить используемый по умолчанию язык для конкретной процедуры, включите имя языка в определение процедуры. Вы можете задать язык процедуры, включив описывающее язык ключе- вое слово в описание процедуры. Например, определение в режиме MASM процедуры PASCAL может иметь вид:
. . . pascalproc PROC PASCAL FAR ; тело процедуры pascalproc ENDP . . .
Турбо Ассемблер использует заданный в процедуре язык для оп- ределения того, какой вход начала и завершения нужно автоматичес- ки включить в тело процедуры. Начальный код устанавливает кадр стека для передаваемых аргументов и локальных переменных процеду- ры, а код завершения перед возвратом из процедуры восстанавливает кадр стека.
Турбо Ассемблер автоматически вставляет в процедуру началь- ный код перед первой инструкцией процедуры или перед первой мет- кой ("метка: цель").
В коде завершения делается следующее:
- в стеке сохраняется текущий регистр BP;
- BP настраивается на текущий указатель стека;
- настраивается указатель стека для выделения локальных пе- ременных;
- в стеке сохраняются регистры, заданные спецификатором USES.
По каждой инструкции RET процедуры Турбо Ассемблер автома- тически вставляет в процедуру код завершения (в случае нескольких инструкций RET код завершения будет вставляться несколько раз). Турбо Ассемблер также вставляет код завершения перед переходом на любой объектно-ориентированный метод (см. Главу 4).
Описание объекта состоит из описаний структур данных объекта и описаний процедур метода, которые вы можете вызывать для данно- го объекта. Описание объекта не означает создание экземпляра объ- екта. Как это сделать, вы узнаете позднее.
Описание базового объекта
Когда вы описываете объект, Турбо Ассемблер создает структу- ру STRUC и описывает данные для этого объекта, и таблицу TABLE, которая описывает методы объекта. Описания данных объекта предс- тавляет собой структуру с тем же именем, что и объект. Описания методов объектов записывается в типе данных TABLE с именем @Table _<имя_объекта>.
Примечание: Более подробно о применении к описанию объ- ектов директивы STRUC рассказывается в Главе 8.
Например, для объекта списка два типа данных описываются следующим образом:
list STRUC описывает следующие элементы:
list_head указатель dword на начало списка list_tail указатель dword на конец списка
@Table_list TABLE описывает следующие методы:
construct указатель dword на процедуру list_construct destroy указатель dword на процедуру
list_destroy и т.д.
Директива STRUC описывает данные для объекта, которые созда- ются каждый раз, когда вы создаете экземпляр объекта. Директива TABLE описывает таблицу используемых по умолчанию для данного описания процедур метода. Турбо Ассемблер поддерживает этот тип данных, он не создает экземпляр таблицы где-либо в памяти прог- раммы. Однако позднее вы увидите, что экземпляр таблицы нужно включить для любого объекта, использующего виртуальные методы.
Приведем пример определений объекта для связанного списка:
list STRUC GLOBAL METHOD { destroy:dword = list_construct ; процедура-конструктор ; списка init:dword = list_init ; процедура-инициализа- ; тор списка deinit:dword = list_deinit ; процедура-деинициали- ; затор списка virtual insert:word = list_insert ; процедура вставки ; узла списка virtual append:word = list_append ; процедура добавле- ; ния узла списка virtual remove:word = list_delete ; процедура удале- ; ния узла списка virtual first:word = list_first ; процедура первого ; узла списка virtual last:word = list_last ; процедура последне- ; го узла списка } list_head dd ? ; указатель начала ; списка list_tail dd ? ; указатель конца ; списка
При описании общедоступного идентификатора вы предполагаете, что он должен быть доступен из других модулей. Общедоступными мо- гут быть следующие типы идентификаторов:
- имена переменных; - метки программы; - числовые константы, определенные с помощь EQU.
Для определения общедоступных идентификаторов можно исполь- зовать директиву PUBLIC, которая имеет следующий синтаксис:
PUBLIC [язык] идентификатор [,[язык] идентификатор] .
где "язык" представляет собой C, CPP, PASCAL, BASIC, FORTRAN, PROLOG или NOLANGUAGE и определяет соглашения конкретного языка, применяемые к имени переменной. Использование спецификатора языка в директиве PUBLIC временно переопределяет временно переопределя- ет текущую установку языка (по умолчанию NOLANGUAGE, если другой язык не задан в директиве .MODEL).
Турбо Ассемблер описывает идентификатор в объектном модуле таким образом, что он будет доступен всем другим модулям. Если вы не сделаете идентификатор общедоступным, то сможете обращаться к нему только из текущего модуля, например:
PUBLIC XYPROC ; процедура общедоступна XYPROC PROC NEAR
При создании процедур методов для объектов применяются неко- торые специальные соглашения. Процедуры методов объектов должны иметь возможность должны иметь возможность доступа к объекту, с которым они работают. Таким образом, в качестве параметра проце- дуры должен использоваться указатель на объект.
Турбо Ассемблер интерпретирует объекты достаточно гибко и позволяет использовать для передачи аргументов процедурам методов разнообразные соглашения. Эти соглашения ограничиваются только необходимостью взаимодействовать с объектами, создаваемыми на языке высокого уровня.
Если вы пишете процедуру метода объекта на самом языке Ас- семблера, то может оказаться полезным использование соглашения, по которому аргументы передаются через регистры. В этом случае вам следует написать процедуру метода, воспринимающую указатель на объект в регистре или паре регистров ES:DI).
Если вы пишете процедуру метода, которая использует соглаше- ния по интерфейсу языка высокого уровня, то процедура должна воспринимать указатель объекта в одном из аргументов. Указатель объекта, передаваемый из объектно-ориентированных языков высокого уровня (таких как С++), представляет собой неявный аргумент, по- мещаемый в начало списка аргументов. Процедура метода, написанная на языке Ассемблера, должна явно включать в список аргументов указатель объекта. В противном случае вы можете получить непред- виденные результаты. Нужно помнить, что в зависимости от того, является ли объект ближним (NEAR) или дальним (FAR), указатель может быть величиной WORD или DWORD.
Когда вы пишете на языке Ассемблера конструктор или деструк- тор, могут возникнуть другие сложности. Чтобы указать, что конс- труктор или деструктор должен выполнять определенные действия, С++ использует (при некоторых обстоятельствах) использует другие неявные аргументы.
Примечание: Об используемых в С++ соглашениях по вызо- ву рассказывается в Главе 18.
Конструкторам, написанным на языке Ассемблера, не обязатель- но требуется передавать указатель на объект. Если объект никогда не распределяется статически, то конструктор объекта всегда будет выделять для объекта память из динамически распределяемой облас- ти.
Назад | Содержание | Вперед
Процедуры NEAR вызываются с помощью вызова ближнего типа и содержат ближний возврат управления. Вы должны вызывать их только в том же сегменте, в котором они определены. Вызов ближнего типа заносит адрес возврата в стек и устанавливает указатель инструк- тор (IP) в значение смешения процедуры. Поскольку сегмент кода (CS) не изменяется, процедура должна находиться в том же сегмен- те, что и вызывающая программа. Когда процессор обнаруживает возврат ближнего типа, он извлекает из стека адрес возврата и снова устанавливает в него IP. Сегмент кода не изменяется.
Процедура FAR вызывается с помощью вызова дальнего типа и содержит возврат дальнего типа. Процедуры FAR вы можете вызывать вне сегмента, в котором они определяются. Вызов FAR заносит в стек адрес в виде сегмента и смещения, а затем устанавливает CS:IP в адрес процедуры. Когда процессор обнаруживает возврат дальнего типа, он извлекает из стека сегмент и смещение адреса возврата и устанавливает в него CS:IP.
Расстояние (NEAR или FAR), используемое в процедуре по умол- чанию, определяется текущей выбранной моделью. Для моделей TINY, SMALL и COMPACT по умолчанию процедура будет ближней (NEAR). Для всех других моделей по умолчанию выбирается расстояние FAR. Если вы не используете упрощенные директивы определения сегментов, то по умолчанию процедура всегда будет ближней (NEAR).
Примечание: FAR или NEAR можно задать в качестве аргу- мента оператора MODEL. Более подробно об этом рассказывает- ся в Главе 7.
Вы можете переопределить используемое по умолчанию расстоя- ние, задав нужное расстояние в определении процедуры. Для этого вы можете использовать ключевые слова NEAR или FAR. Эти ключевые слова переопределяют расстояние, используемое в процедуре по умолчанию, но только для текущей процедуры. Например:
. . . MODEL TINY ; по умолчанию расстояния NEAR . . . ; test1 - это дальняя процедура test1 PROC FAR ; тело процедуры RET ; это будет дальним возвратом: ENDP ; test2 по умолчанию является ; ближней процедурой test2 PROC ; тело процедуры
Процедура метода работает с экземплярами объекта. Они очень напоминают библиотечные подпрограммы, поскольку должны иметь хо- рошо определенный вызов и возвращать в виде интерфейса значение, но знание внутренней организации процедуры метода не требуется.
Процедуры метода для объекта должны обеспечивать исчерпываю- щее обслуживание объектов, то есть, они должны быть единственными процедурами, для который разрешен непосредственный доступ к объ- ектам. Кроме того, при построении методов следует использовать принципы абстрактных данных: вы должны иметь возможность вызова процедур метода без необходимости знать о том, как они работают и какую имеют внутреннюю организацию.
Что касается всех других аспектов, то вы можете писать про- цедуры методов на любом известном вам языке или интерфейсе, хотя обычно используются соглашения по вызову C++ или Паскаля. Аргу- менты процедур также выбираются по вашему усмотрению. Обычно не- обходимым является один аргумент - указатель на экземпляр объек- та. Некоторые процедуры методов могут потребовать дополнительных параметров. Например, инициализация метода для объекта списка требует просто указатель на объект списка, в то время как метод включения в список требует указатель на список, указатель на но- вый узел для вставки и указатель на узел, включаемый после него.
В использовании статических и виртуальных методов есть свои достоинства и недостатки. Статические методы определяются на эта- пе компиляции, а результатом будет непосредственный вызов проце- дуры метода. Это позволяет выполнить вызов быстрее и не требует использования промежуточных регистров (как при использовании вир- туальных методов). Однако, поскольку эти вызовы определяются на этапе компиляции, вызовы статических методов не обладают гиб- костью вызовов виртуальных методов.
Вызовы виртуальных методов выполняются непосредственно через реализацию таблицы виртуальных методов объекта. Тот факт, что вы- зов является косвенным, приводит к тому недостатку, что при вы- полнении вызова требуется использование промежуточных регистров (что может усложнить код программы). Однако большим преимуществом является то, что вызовы виртуальных методов определяются на этапе выполнения. Таким образом, вы можете выполнять вызовы виртуальных методов для порожденного объекта с помощью вызова метода общего объекта-"предка". При этом не требуется точно знать, с каким ви- дом объекта-потомка вы имеете дело.
Некоторые директивы позволяют вам описывать сложные адресные подтипы. Эти выражения типов аналогичны тем, которые используют- ся в языке Си, поскольку они представляют несколько уровней кос- венности указателя. Например, сложное выражение типа:
PTR WORD
представляет указатель на слово. (Размер указателя зависит от размера модели сегментации, которую вы выбираете с помощью дирек- тивы MODEL.)
Сводный синтаксис сложных адресных подтипов приведен в Таб- лице 5.6.
Сложные адресные подтипы Таблица 5.6 -------------------------------------T--------------------------¬ ¦ Синтаксис ¦ Значение ¦ +------------------------------------+--------------------------+ ¦ простой_адресный_подтип ¦ Подтип заданного адреса. ¦ ¦ ¦ ¦ ¦ [раст]PTR[сложный_адресный_подтип]¦ Указатель на заданный ¦ ¦ ¦ сложный адресный подтип, ¦ ¦ ¦ размер которого опреде- ¦ ¦ ¦ ляется текущей директи- ¦ ¦ ¦ вой MODEL или заданным ¦ ¦ ¦ расстоянием (если они ¦ ¦ ¦ присутствуют). ¦ L------------------------------------+---------------------------
Необязательный параметр расстояния вы можете описать следую- щим путем:
Синтаксис расстояния Таблица 5.7 --------------------T-------------------------------------------¬ ¦ Синтаксис ¦ Значение ¦ +-------------------+-------------------------------------------+ ¦ NEAR ¦ Используется ближний указатель, который ¦ ¦ ¦ может быть 16 или 32-разрядным, в зависи- ¦ ¦ ¦ мости от текущей модели. ¦ ¦ ¦ ¦ ¦ FAR ¦ Используется дальний указатель, который ¦ ¦ ¦ может быть 32 или 48-разрядным, в зависи- ¦ ¦ ¦ мости от текущей модели. ¦ ¦ ¦ ¦ ¦ SMALL NEAR ¦ Используется 16-разрядный указатель ¦ ¦ ¦ (только для процессоров 80386 и 80486). ¦ ¦ ¦ ¦ ¦ LARGE NEAR ¦ Используется 32-разрядный указатель ¦ ¦ ¦ (только для процессоров 80386 и 80486). ¦ ¦ ¦ ¦ ¦ SMALL FAR ¦ Используется 32-разрядный дальний указа- ¦ ¦ ¦ тель (только для процессоров 80386 и ¦ ¦ ¦ 80486). ¦ ¦ ¦ ¦ ¦ LARGE FAR ¦ Используется 48-разрядный дальний указа- ¦ ¦ ¦ тель (только для процессоров 80386 и ¦ ¦ ¦ 80486). ¦ L-------------------+--------------------------------------------
Тип указываемого объекта в комплексных ссылочных типах не является строго обязательным. Турбо Ассемблеру нужно знать только размер типа. Таким образом, в сложных ссылочных типах (но не в простых типах) допускаются опережающие ссылки.
Многие инструкции позволяют вам различать адрес и содержимое адреса. Вы можете делать это, используя квадратные скобки. Напри- мер: MOV AX,BX ; переместить BX в AX MOV AX,[BX] ; переместить в AX содержимое по ; адресу BX
Приведем общий синтаксис, в котором используются квадратные скобки:
[выражение]
В режиме MASM квадратные скобки для выражений, являющихся адресами, не обязательны. В качестве операнда инструкции процес- соров 80х86 не может использоваться полный адрес. Вместо этого используется только сегмент (полученный с помощью операции SEG) или смещение (полученное с помощью операции OFFSET).
Если ясно, что выражение является адресом, а квадратные скобки не используются, то в режиме Ideal выводится предупреждаю- щее сообщение. Это предупреждение можно запретить (см. Главу 12). Однако хорошей практикой программирования является использование скобок.
Может оказаться так, что вам потребуется использовать неко- торые переменные или процедуры во всех модулях вашей программы. Турбо Ассемблер обеспечивает несколько директив, которые позволя- ют вам определить идентификаторы и библиотеки таким образом, что вы сможете использовать их глобально, а также использовать общие переменные (для которых выделяет память компоновщик). Вам потре- буется также соблюдать аккуратность при назначении имен идентифи- каторов, поскольку различные языки имеют здесь конкретные требо- вания. В следующий нескольких разделах мы обсудим эти директивы и соглашения по наименованию.
Иногда (например, в макрокоманде) бывает полезно определить характеристики заданного выражения. Для этого служат операции SYMTYPE и .TYPE.
В режиме Ideal используется следующий синтаксис:
SYMTYPE выражение
В режиме MASM используется следующий синтаксис:
.TYPE выражение
Операции SYMTYPE и .TYPE возвращают описывающее выражение значение-константу. Это значение разбивается на битовые поля, по- казанные в следующей таблице:
Битовые поля операций SYMTYPE и .TYPE Таблица 5.21 --------T-------------------------------------------------------¬ ¦ Бит ¦ Значение ¦ +-------+-------------------------------------------------------+ ¦ 0 ¦ Выражение является относительным указателем памяти в ¦ ¦ ¦ программе. ¦ ¦ ¦ ¦ ¦ 1 ¦ Выражение является относительным указателем на дан- ¦ ¦ ¦ ные в программе. ¦ ¦ ¦ ¦ ¦ 2 ¦ Выражение представляет собой значение-константу. ¦ ¦ ¦ ¦ ¦ 3 ¦ Выражение использует прямой режим адресации. ¦ ¦ ¦ ¦ ¦ 4 ¦ Выражение содержит регистр. ¦ ¦ ¦ ¦ ¦ 5 ¦ Идентификатор определен. ¦ ¦ ¦ ¦ ¦ 7 ¦ Выражение содержит идентификатор, определенный извне.¦ L-------+--------------------------------------------------------
Если биты 2 и 3 равны нулю, то выражение содержит косвенный регистр ([BX]).
Если Турбо Ассемблер не может вычислить выражение, то опера- ция SYMTYPE возвращает соответствующие ошибки. Однако операция .TYPE в этих случаях будет возвращать значение (обычно 0).
Именованные типы представляют простые или сложные типы. Для определения именованных типов вы можете использовать директиву TYPEDEF. Приведем синтаксис режима Ideal.
TYPEDEF имя_типа сложный_тип
В режиме MASM синтаксис следующий:
имя_типа TYPEDEF сложный_тип
где "сложный_тип" описывает любой тип или указатель нескольких уровней косвенности. Подробнее о сложных типах рассказывается в Главе 5. "Имя_типа" определяет имя заданного типа.
Когда вы в выражении указываете названный тип, он действует так же, как простой тип соответствующего размера. Например:
MOV ax, word ptr [bx] ; простой оператор foo TYPESEF near ptr byte ; FOO - это слово MOV ax, foo ptr [bx] ; поэтому это тоже работает
Метки позволяют вам присваивать значения идентификаторам. Существует три способа определения меток:
- использование операции :; - использование директивы LABEL; - использование операции :: (MASM 5.1).
Директива LABEL позволяет вам создавать элементы структур без выделения данных. Обычно LABEL задает метку с именем или мар- кер в той точке, где она обнаруживается в сегменте. Директива LABEL внутри определения структуры определяет элементы этой структуры. Приведем синтаксис директивы LABEL:
LABEL имя сложный_тип
В режиме MASM вы можете использовать следующий синтаксис:
имя LABEL сложный_тип
где "имя" - это имя элемента структуры. "Тип" - это желаемый тип элемента структуры. Он может представлять собой любое допустимое имя типа. Описание имеющихся спецификаторов типов можно найти в Главе 5.
Объект содержит структуру данных и список соответствующих объекту методов. Турбо Ассемблер используется для представления связанной с объектом структуры структурный тип данных, а для представления связанного с объектом списка методов - табличный тип данных.
Определить объекты вам поможет директива STRUC. В режиме Ideal используется следующий синтаксис:
STRUC имя [модификаторы] [имя_порождающего_объекта]
[METHOD [элемент_таблицы [,элемент_таблицы.]]] элементы_структуры ENDS [имя]
В режиме MASM используется следующий синтаксис:
имя STRUC [модификаторы] [имя_порождающего_объекта] [METHOD [элемент_таблицы [,элемент_таблицы.]]] элементы_структуры ENDS [имя]
где "имя" - это имя объекта. "Имя_порождающего_объекта" - это не- обязательное имя порождающего объекта. (Турбо Ассемблер явно под- держивает только одиночное наследование.) Структура данных порож- дающего объекта будет автоматически включаться в структуру данных нового объекта, а таблица методов порождающего объекта - в табли- цу методов нового объекта.
Каждое поле "элемент_таблицы" описывает имя метода и проце- дуры метода, связанного с объектом. Синтаксис этого поля такой же, как в определении таблицы.
Поле "элементы_структуры" описывает дополнительные элементы структуры, которые вы хотите определить в структуре данных объек- та. Они форматируются точно также, как в открытом определении структуры.
Необязательное ключевое слово "модификаторы" может представ- лять собой одно из следующих слов:
Разрешенные модификаторы Таблица 8.3 ---------------------T------------------------------------------¬ ¦ Ключевое слово ¦ Значение ¦ +--------------------+------------------------------------------+ ¦ GLOBAL ¦ Приводит к тому, что адрес таблицы вир- ¦ ¦ ¦ туальных методов (если она имеется) бу- ¦ ¦ ¦ дет объявлен общедоступным. ¦ ¦ ¦ ¦ ¦ NEAR ¦ Указатель виртуальной таблицы (если она ¦ ¦ ¦ имеется будет содержать смещение (16 или ¦ ¦ ¦ 32, бита в зависимости от выбора текущей ¦ ¦ ¦ модели по USE16 или USE32). ¦ ¦ ¦ ¦ ¦ FAR ¦ Указатель виртуальной таблицы (если она ¦ ¦ ¦ имеется будет содержать величины, задаю- ¦ ¦ ¦ щие сегмент и смещение (32 или 48 бит, в ¦ ¦ ¦ зависимости от выбора текущей модели по ¦ ¦ ¦ USE16 или USE32). ¦ L--------------------+-------------------------------------------
Если вы не задаете модификатор, размер указателя виртуальной таблицы (если он имеется) зависит от того, адресуются ли данные в текущей модели как NEAR, или как FAR.
Общие переменные действуют как внешние переменные, но здесь есть одно существенное различие: общие переменные распределяются компоновщиком. Общие переменные переменные на самом деле аналоги- чны глобальным переменным, но вы не можете присвоить им начальные значения. На эти неинициализированные переменные можно ссылаться из нескольких модулей.
Для определения общей переменной вы можете использовать ди- рективу COMM. Она имеет следующий синтаксис:
COMM определение [,определение].
Каждое "определение" описывает идентификатор и имеет следую- щий формат:
[расстояние] [язык] имя_идентификатора[[счетчик_1]]: сложный_тип [счетчик_2]
где поле "расстояние" не является обязательным и может быть FAR или NEAR. Если вы не зададите "расстояние", по умолчанию оно бу- дет соответствовать используемой модели памяти. Если вы не ис- пользуете упрощенные директивы сегментации, то по умолчанию ис- пользуется NEAR. В крошечной, малой и средней моделях по умолчанию также используется NEAR, а во всех других - FAR.
Поле "язык" задает язык: C, PASCAL, BASIC, FORTRAN, PROLOG или NOLANGUAGE. Использование в директиве COMM спецификатора язы- ка временно переопределяет текущую установку языка (по умолчанию или заданную с помощью директивы .MODEL). Заметим, что для того, чтобы действовало данное средство, вам не обязательно использо- вать директиву .MODEL.
Поле "имя_идентификатора" задает имя идентификатора, который должен быть общим, и память для которого должна выделяться на этапе компоновки. В этом поле можно также задавать коэффициент размера элемента массива "счетчик_1", который должен учитываться в вычисляемом объеме общей памяти. Если в качестве расстояния за- дано FAR, то компоновщик, использует значение "счетчика_2", чтобы указать, сколько имеется элементов с размером, равным произведе- нию основного размера элемента (который определяется его типом) на "счетчик_1". По умолчанию значение поля "счетчик_1" равно 1.
Большинство прикладных программ могут использовать сегменты, создаваемые стандартными моделями. Однако гибкость стандартных моделей ограничена. В некоторых прикладных программах необходим полный контроль за генерацией сегментов. Эти возможности предос- тавляют общие сегментные директивы.