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

         

Need right angle bracket


(Отсутствует правая угловая скобка)

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

MYSTRUC STRUCNAME <1,2,3



Need right curly bracket


(Требуется правая фигурная скобка)

Эта ошибка возникает в структуре, таблице или записи, когда ожидается }, но она не найдена.



Need right parenthesis


(Отсутствует правая круглая скобка)

Выражение содержит левую круглую скобку без парной ей правой угловой скобки. Например:

Х = 5 * (4 + 3

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



Need right square bracket


(Отсутствует правая квадратная скобка)



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

mov ax,[SI ; ошибка: нет закрывающей ; скобки (]) после SI

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



Need stack argument


(Не указан стековый параметр)

В команде арифметики с плавающей запятой не указан второй операнд, т.е. операнд, указываемый после запятой. Например:

FADD ST,



Need structure member name


(Не указано имя поля структуры)

В режиме 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



Not expecting group or segment quantity


(Использование имени группы или сегмента недопустимо)

Указано имя группы или сегмента там, где это недопустимо. Например:

CODE SEGMENT rol ax,CODE ; ошибка: здесь нельзя указывать ; имя сегмента



с целевым регистром, инструкция SETFIELD


Турбо ассемблер версии 3.1 предусматривает следующие новые средства:

1. Расширенные инструкции SETFIELD и GETFIELD.

Когда исходное значение совпадает с целевым регистром, инструкция SETFIELD больше не выполняет операцию OR над исходным значением. Вместо этого SETFIELD будет обеспечи- вать, что неустановленные поля целевого региста будут ну- левыми.

Аналогично, если вы используете при совпадении исходного и целевого регистра инструкцию GETFIELD, то инструкция больше не будет генерировать нефункциональную инструкцию "MOV приемник, источник".

2. Дополнительные параметры командной строки.

Турбо ассемблер 3.1 позволяет теперь задавать дополни- тельные объектные форматы, используемые отдельными компо- новщиками. Это следущие параметры:

----------------T-----------------------------------------------¬ ¦ Параметр ¦ Значение ¦ +---------------+-----------------------------------------------+ ¦ /o ¦ Генерируются стандартные совместимые с TLINK¦ ¦ ¦ объектные файлы с разрешенной поддержкой¦ ¦ ¦ оверлеев. ¦ ¦ ¦ ¦ ¦ /oi ¦ Генерируются объектные файлы в формате, сов-¦ ¦ ¦ местимом с компоновщиком IBM. ¦ ¦ ¦ ¦ ¦ /op ¦ Генерируются объектные файлы в формате, сов-¦ ¦ ¦ местимом с компоновщиком Phar Lap. ¦ ¦ ¦ ¦ ¦ /os ¦ Генерируются стандартные совместимые с TLINK¦ ¦ ¦ объектные файлы с запрещенной поддержкой¦ ¦ ¦ оверлеев. Этот параметр задается по умолча-¦ ¦ ¦ нию. ¦ L---------------+------------------------------------------------

3. Новый атрибут сегмента и предупреждение о неинициализиро- ванном сегменте.

Атрибут сегмента UNINIT позволяет вам выявлять непредна- меренную запись инициализированных данных в неинициализи- рованные сегменты данных. Использование данного атрибута в описании сегмента позволяет вам генерировать предупреж- дающее сообщение, уведомляющее об этой проблеме.

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

BSS SEGMENT PUBLIC WORD UNINIT 'BSS'

Данная директива будет генерировать следующее предупреж- дающее сообщение:

Warning: Data or code written to uninitialized segment (Предупреждение: Данные или код записываются в неини- циализированный сегмент)

Для запрещения данного сообщения используйте директиву NOWARN UNI. Вновь разрешить это сообщение можно с по- мощью директивы WARN UNI.

4. Директивы PUSHSTATE и POPSTATE.

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

Сохраняемая Турбо ассемблером информация о состоянии со- держит:

- текущую версию эмуляции (например, T310); - выбор режима (например, IDEAL, MASM, QUIRKS, MASM51); - переключатели EMUL и NOEMUL; - выбор текущего процессора и сопроцессора; - переключатели MULTERRS или NOMULTERRS; - переключатели SMART или NOSMART; - текущее основание системы счисления; - переключатели JUMPS или NOJUMPS; - переключатели LOCALS или NOLOCALS; - текущий префикс локального символа.

Для возврате последнего сохраненного состояния из стека используйте директиву POPSTATE.

; примеры директив PUSHSTATE и POPSTATE

ideal model small codeseg

jumps locals @@

; Показывает изменение состояние процессора, основание ; чисел и режим JUMPS pushstate nojumps radix 2 ; Установить в двоичное основание p386 jl next1 ; После этого нет дополнительных NOPS mov eax,100 ; Теперь 100 означает двоичное число ; 100 или десятичное 4. next1: popstate ; Восстановить JUMPS и режим не 386.

; Вернуться к директиве JUMPS, не 386 и десятичному осно- ; ванию

jl next2 ; Три дополнительных NOPS для ; обработки JUMPS xor eax,eax ; Теперь не режим 386!

mov cx,100 ; Теперь 100 означает десятичное 100

pushstate MULTERRS mov ax,[bp+abc popstate

mov ax,[bp+abc

; Показазывает запрещение локальной области действия ; идентификаторов locals next2: @@a: loop @@a next3: @@a: loop @@a ; Допутимо благодаря области действия ; NEXT2: и NEXT3:

pushstate nolocals next4: @@b: loop @@b next5: @@b: loop @@b ; Это приведет к конфликту из-за ; нелокальности popstate

; Показывает изменение префикса локального идентификатора ; и режим MASM/IDEAL pushstate masm locals @$



testproc proc ; Режим MASM для описания процедуры jmp @$end

@$end: nop @@end: ret testproc endp

testproc2 proc jmp @$end @$end: nop ; Это не приводит к конфликту с меткой ; в TESTPROC @@end: ret ; Эта метка приведет к конфликту testproc2 endp popstate

; Теперь вернемся к @@ в качестве локального префикса и ; режиму IDEAL testproc2b proc ; Это будет работать, так как мы снова ; в режиме IDEAL ret testproc2b endp ; а это даст ошибку.

proc testproc3 jmp @$end2 @$end2: nop @@end2: ret endp testproc3

proc testproc4 jmp @$end2 @$end2: nop ; Эта метка не приведет к конфликту @@end2: ret ; Эта метка не приведет к конфликту ; с меткой в TESTPROC3 endp testproc4 end

5. Новые директивы процессора.

Новые директивы процессора добавлены для работы с платой 486SX.

.487 - разрешает ассемблирует инструкций числового про- цессора 487. Данная инструкция работает только в режиме MASM.

P487 - разрешает ассемблирует инструкций числового про- цессора 487. Данная инструкция работает как в ре- жиме MASM, так и в режиме Ideal.

6. Подстановка текстовых присваиваний.

Для улучшения совместимости с MASM в TASM 3.1 введены из- менения в характер постановки текстовых присваиваний. Это приводит к тому, что старый код дает в TASM 3.1 ошибки, что можно устранить двумя способами:

1) Используйте директиву командной строки /UT300 для вы- бора обработки в стиле TASM 3.0.

2) В начале строк, которые вызывают ошибки в TASM 3.1, но не в TASM 3.0, явно используйте оператор текстовой макроподстановки %.

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

TASM /dMDL=small iwhereis.asm

Затем в IWHEREIS.ASM, код делает проверку, чтобы убедить- ся, что определено MDL, и, если это так, в TASM 3.0 ис- пользуется следующая строка кода:

model MDL

TASM 3.0 перед вычислением выражения автоматически подс- тавляет значение MDL. TASM 3.1 теперь требует от вас яв- ного указания оператора %:

% model MDL


О данной документации


Описание Турбо Ассемблера поставляется в виде двух пособий: "Руководства пользователя по Турбо Ассемблеру" (данный текст) и "Краткого справочного руководства по Турбо Ассемблеру". В "Руко- водстве пользователя" даются основные инструкции по использованию Турбо Ассемблера, организации его интерфейса с другими языками и описываются предопределенные операции, идентификаторы и директи- вы, используемые Турбо Ассемблером. "Краткое справочное руководс- тво" позволяет быстро находить информацию по инструкциям процес- сора и сопроцессора и директивам.

Рассмотрим содержание "Руководства пользователя" более под- робно.



Обеспечение стека


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

Есть два способа настройки стека. Вы можете использовать ин- струкцию 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


В 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

Каждый формальный аргумент имеет следующий синтаксис:

имя_формального_аргумента[:тип_аргумента]

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


Типы формальных аргументов Таблица 14.1 -----------------------T----------------------------------------¬ ¦ Тип ¦ Значение ¦ +----------------------+----------------------------------------+ ¦ REQ ¦ Аргументы не могут быть нулевыми или ¦ ¦ ¦ состоять из пробелов. ¦ ¦ ¦ ¦ ¦ =<текстовая_строка> ¦ Заключенная в кавычки текстовая строка ¦ ¦ ¦ задает используемое по умолчанию зна- ¦ ¦ ¦ чение аргумента (когда фактический ар- ¦ ¦ ¦ гумент является нулевым или состоит из ¦ ¦ ¦ пробелов). ¦ ¦ ¦ ¦ ¦ VARARG ¦ Фактический аргумент состоит из ос- ¦ ¦ ¦ тальной части макровызова, интерпрети- ¦ ¦ ¦ руемого как список аргументов. Для ¦ ¦ ¦ обеспечения этой интерпретации исполь- ¦ ¦ ¦ зуются угловые скобки, запятые. ¦ ¦ ¦ ¦ ¦ REST ¦ Фактический аргумент состоит из ос- ¦ ¦ ¦ тальной части макровызова, интерпрети- ¦ ¦ ¦ руемого как непосредственный текст. ¦ L----------------------+-----------------------------------------


Одно- и двухпроходные режимы ассемблирования


В стандартном режиме Турбо Ассемблер выполняет ассемблирова- ние за один проход, а MASM - за два прохода. Поэтому Турбо Ас- семблер - более быстрый ассемблер по сравнению с MASM. Однако од- нопроходность Турбо Ассемблера может привести к некоторой его несовместимости с MASM при разрешении ссылок вперед и обработке конструкций, зависящих от прохода. В TASM имеется параметр ко- мандной строки (/m), с помощью которого можно задать число прохо- дов. Если требуется обеспечить максимум совместимости с MASM, то нужно указать двухпроходный режим (/m2). (См. Главу 2, где данный параметр обсуждается более подробно.)

Использование данного параметра командной строки будет гене- рировать режим, совместимый с MASM (два прохода), когда присутс- твуют следующие конструкции:

- директивы IF1 и IF2;

- директивы ERR1 и ERR2;

- ссылки вперед с IFDEF и IFNDEF;

- опережающие ссылки с операцией .TYPE;

- рекурсивно определенные числа, такие, как

NMBR=NMBR+1;

- рекурсивно определенные текстовые макрокоманды или тексто- вые макрокоманды, на которые имеются опережающие ссылки, такие, как:

LNAME CATSTR LNAME,<1>

- макрокоманды, на которые имеются опережающие ссылки.



Ограничения при использовании объектов типа EXTRN


Синтаксис уточненного идентификатора Турбо Паскаля, при ко- тором для доступа к объекту в заданном модуле используется имя модуля и точка, несовместим с синтаксическими правилами Турбо Ас- семблера и будет, таким образом, отвергнут. Описание:

EXTRN SYSTEM.Assing : FAR

приведет к тому, что Турбо Ассемблер выдаст сообщение об ошибке.

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

EXTRN PublicProc : FAR

то не сможете записать оператор вида:

call PublicProc + 42

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

EXTRN i : WORD

то не сможете использовать в модуле Турбо Ассемблера выражения LOW i или HIGH i.



One non-null field allowed per union expansion


(При расширении объединения допускается указывать только од- но поле непустым)

При инициализации объединения, определенного директивой UNION, указано более одного значения. Например:

U UNION DW ? DD ? ENDS UINST U <1,2> ;ошибка: можно указать <?,2> либо <1,?>

В объединении можно инициализировать только одно поле.



Only one startup sequence allowed


(Допускается только одна директива генерации кода инициали- зации)

Это сообщение выдается, если в модуле указано более одной директивы .STARTUP или STARTUPCODE.



Open conditional


(Открытый условный блок)

Обнаружена директива END, означающая конец исходного файла. Однако условно ассемблируемый блок, открытый одной из директив вида IFxxx, не был закрыт директивой ENDIF. Например:

IF BIGBUF END ; нет директивы ENDIF перед директивой END

Эта ошибка обычно выдается, если вместо директивы окончания условного блока ENDIF, ошибочно указана директива END.



Open procedure


(Открытая процедура)

Обнаружена директива END, означающая конец исходного файла. Однако блок описания процедуры, открытый директивой PROC, не был закрыт директивой ENDР. Например:

MYFUNC PROC END ; нет директивы ENDР перед директивой END

Эта ошибка обычно выдается, если вместо директивы конца бло- ка процедуры - ENDP ошибочно указана директива END.



Open segment


(Открытый сегмент)

Обнаружена директива END, означающая конец исходного файла. Однако сегмент, открытый директивой SEGMENT, не был закрыт дирек- тивой ENDS. Например:

DATA SEGMENT END ; нет директивы ENDS перед директивой END

Эта ошибка обычно выдается, если вместо директивы конца сег- мента - ENDS ошибочно указана директива END.



Open structure definition


(Не указан конец определения структуры)

Обнаружена директива 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


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

BOUNDS DW 1,4 ; нижняя и верхняя границы BOUND DWORD PTR BOUNDS ; требуется в режиме MASM

Однако в режиме Ideal требуется только записать:

BOUNDS DW 1,4 ; нижняя и верхняя границы BOUND [BOUNDS] ; допускается в ; режиме Ideal



Operand types do not match


(Не совпадают типы операндов)

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

ABC DB 5 . mov ax,ABC



Operation illegal for static table member


(Для статического элемента таблицы операция не допускается)

Для получения адреса статического элемента таблицы использо- вана операция точки. Это не допускается.



Оперативный справочник


Оперативную справочную информацию по Турбо Ассемблеру можно получить с помощью резидентной с памяти справочной программы-ути- литы 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).


Завершающий код изменяет действие кода инициализации на об- ратное. Это делается следующий образом:
- из стека извлекаются регистры, заданные оператором USES;
- настраивается указатель стека, чтобы отбросить локальные аргументы;
- из стека извлекается сохраненный регистр BP;
- стек настраивается, чтобы отбросить переданные аргументы (если этого требует язык) и выполнить возврат.
Последний шаг кода завершения (отбрасывание аргументов) вы- полняется только для тех языков, которые требуют удаления аргу- ментов процедуры (например, Бейсик, Фортран, Паскаль). По согла- шениям, принятым в других языках (Си, С++, Пролог), аргументы ос- таются в стеке, и удалять их должна вызывающая программа.
Турбо Ассемблер всегда реализует код начала и завершения процедуры с помощью наиболее эффективных инструкций текущего выб- ранного языка и процессора.
Для процедур NOLANGUAGE Турбо Ассемблер не генерирует код начала и завершения. Если в таких процедурах ожидается передача аргументов в стеке, вы должны сами включать код начала и заверше- ния.
В общем случае язык процедуры влияет на процедуру следующим образом:
Как язык влияет на процедуру Рис. 10.1 ----------T------T------T--------T--------T-------T------T------¬ ¦ Язык ¦ Нет ¦Бейсик¦ Фортран¦ Паскаль¦ Си ¦ С++ ¦Пролог¦ +---------+------+------+--------+--------+-------+------+------+ ¦ Порядок ¦ L-R ¦ L-R ¦ L-R ¦ L-R ¦ R-L ¦ R-L ¦ R-L ¦ ¦ аргу- ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ментов ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ (слева ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ -напра- ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ во, ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ справа- ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ налево) ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ +---------+------+------+--------+--------+-------+------+------+ ¦Кто очи- ¦проце-¦проце-¦процеду-¦процеду-¦вызыва-¦вызы- ¦вызы- ¦ ¦щает стек¦дура ¦дура ¦ра ¦дура ¦ющая ¦вающая¦вающая¦ ¦(вызыва- ¦ ¦ ¦ ¦ ¦прог- ¦прог- ¦прог- ¦ ¦ющая ¦ ¦ ¦ ¦ ¦рамма ¦рамма ¦рамма ¦ ¦программа¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦или про- ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦цедура) ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ L---------+------+------+--------+--------+-------+------+-------
L-R - слева-направо, R-L - справа-налево.
Для включения в файл листинга кода начала и завершения вы можете использовать параметр командной строки /la. Это позволит вам увидеть разницу между языками.
Примечание: Более подробную информацию можно найти в Главе 13.

Описание объектов


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

Описание базового объекта

Когда вы описываете объект, Турбо Ассемблер создает структу- ру 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 ? ; указатель конца ; списка


ENDS
Примечание: Ключевое слово METHOD показывает, что вы используете расширенную форму директивы STRUC и определяете объект с именем list (список).
Примечание: Каждая запись состоит из имени метода, двоеточия, размера указателя на процедуру метода (WORD для ближних процедур, DWORD для дальних процедур). Далее следу- ет символ равенства, имя процедуры и вызов этого метода.
Давайте возьмем данный пример и посмотрим, что происходит.
Ключевое слово METHOD указывает на вызов метода. За ним сле- дует описаний процедур метода для данного объекта. Поскольку спи- сок методов занимает более одной строки, описания заключаются в фигурные скобки ({ }).
Каждое описание метода сообщает Турбо Ассемблеру, какую про- цедуру ему следует использовать для работы с объектом при вызове имени данного метода.
Например, первое описание процедуры метода:
construct:dword = list_construct
объявляет метод с именем consrtruct, которая является процедурой дальнего типа (указатель в нее записывает DWORD). Фактическим именем процедуры метода является list_construct, что следует оп- ределить где-либо еще в исходном коде. Турбо Ассемблер рассматри- вает метод как виртуальный, если ему предшествует ключевое слово VIRTUAL. Когда вы вызываете такой метод, Турбо Ассемблер будет искать адрес процедуры метода, извлекая его из таблицы, которая присутствует в памяти во время загрузки. В противном случае метод является статическим методом. Это означает, что Турбо Ассемблер может определить его адрес на этапе компиляции. Например, метод construct является статическим методом, а метод insert описывает- ся как виртуальный метод. Позднее в этой главе мы поясним, для чего может понадобиться выбирать виртуальные или статические ме- тоды.
Далее за разделом описания процедуры метода непосредственно следует структура данных. Это определение использует синтаксис стандартной директивы STRUC. Данный пример содержит описания на- чала и конца списка.
В части описания метода в описании объекта, пока вы не ис- пользуете виртуальные методы, никаких данных в структуру данных объекта не заносится. Эти описания приводят к тому, что Турбо Ас- семблер будет строить отдельную таблицу структуры данных, которая содержит в качестве используемых по умолчанию значений адреса за- данных процедур метода. Для каждого объекта вы должны иметь эк- земпляр данной таблицы, и должны явно поместить таблицу. Как это сделать, мы поясним в данной главе позднее.


Так как описание должно присутствовать в модуле, содержащем процедуры метода для объекта (а также включаться в любой исполь- зующий объекты исходный код), вы должны описать сам объект в от- дельном файле, который может включаться (INCLUDE) в ваш исходный код.
Мы рекомендуем вам использовать имя файла в виде имя_объек- та.ASO (что означает ассемблируемый объект). В этот файл следует включать только описание объекта. Методы объекта следует разме- щать в другом исходном файле, благодаря чему вы можете включать описание объекта там, где это потребуется. Например, описание объекта связанного списка в предыдущем примере можно было бы раз- местить в файле LIST.ASO. Для определения процедур метода объекта можно использовать файл LIST.ASM. В любую программу, использующую объекты, можно было бы включить LIST.ASO, но не LIST.ASM.
Ключевое слово GLOBAL в описании объекта приводит к тому, что Турбо Ассемблер делает эту информацию общедоступной. Это поз- воляет вам использовать объект в модуле, отличном от того модуля, в котором он определен. Во все использующие объект модули должно включаться описание объекта.
Описание порожденного объекта
Объект, который наследует методы и данные другого объекта, называется порожденным (производным) объектом. Вы не можете пере- определить элементы структуры данных порождающего объекта, но мо- жете переопределить отдельные методы, задавая их в списке методов нового объекта.
Независимо от того, является ли объект базовым или порожден- ным от другого объекта, его может наследовать другой объект. Нас- ледуемый объект называется порождающим (родительским) объектом. Порожденный объект наследует данные и методы порождающего объек- та, поэтому вам следует использовать наследование только в тех случаях, когда эти методы и данные полезны для нового объекта.
Например, вы можете определить объект очереди, который нас- ледует объект связанного списка (поскольку очередь можно реализо- вать в виде связанного списка). Приведем пример такого порожден- ного объекта:


queue STRUC GLOBAL list METHOD { init:DWORD=queue_init virtual insert:word = queue_insert ; (процедура вставки ; узла очереди) virtual remove:word = queue_delete ; (процедура удаления ; узла очереди) virtual first:word = queue_first ; (процедура первого ; узла очереди) virtual last:word = queue_last ; (процедура послед- ; него узла очереди) virtual enqueue:word = list_append ; процедура постановки ; в очередь virtual dequeue:word = queue_dequeue ; процедура удаления ; из очереди
Размещение перед ключевым словом METHOD объекта с именем list (список) указывает Турбо Ассемблеру, что новый объект queue (очередь) наследует методы и данные объекта list. Размещенный в этом месте объект с любым именем будет наследоваться описываемым объектом. Допускается использовать только одно имя (поддерживает- ся только одиночное наследование).
Новый объект queue наследует все данные и методы объекта списка list (если вы их не переопределяете). Заметим, что в queue для установки указателя на таблицу виртуальных методов для очере- дей требуется своя собственная процедура init.
Наследуемые для очереди описания методов insert, remove, first и last вновь специфицируются в описании, поэтому данные ме- тоды заменяются указанными процедурами.
Для очереди описываются два новых метода: enqueue и dequeue. Заметим, что процедура метода для enqueue (добавление в конец очереди) представляет собой то же самое, что присоединение к кон- цу списка. Однако для извлечения из очереди (отмены постановки в очередь) нужна новая процедура, которая называется queue_dequeue.
Объект queue не содержит дополнительных данных, отличных от тех, которые наследуются из списка list. Он наследует указатели начала и конца связанного списка, которые для очереди также нуж- ны, поскольку методы связанного списка используются для обслужи- вания очереди.

Описание общедоступных идентификаторов


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

- имена переменных; - метки программы; - числовые константы, определенные с помощь 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 или FAR


Процедуры 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 ; тело процедуры


RET ; это будет ближним возвратом ENDP . . .
В процедурах NEAR и FAR используется одна и та же инструкция RET. Турбо Ассемблер использует расстояние процедуры для опреде- ления того, требуется возврат ближнего или дальнего типа. Анало- гично, Турбо Ассемблер использует расстояние процедуры для опре- деления того, требуется для ссылки на процедуру возврат ближнего или дальнего типа.
. . . CALL test1 ; это дальний возврат CALL test2 ; это ближний возврат . . .
При выполнении вызова процедуры с опережающей ссылкой Турбо Ассемблеру может потребоваться для определения расстояния проце- дуры выполнить несколько проходов. Например:
. . . test1 PROC NEAR MOV ax,10 CALL test2 RET test1 ENDP
test1 PROC FAR ADD ax,ax RET test2 ENDP . . .
Когда Турбо Ассемблер при первом проходе достигает инструк- ции call test2, он еще не обнаруживает test2, и следовательно не знает расстояния. Он предполагает, что это расстояние NEAR, и что можно сделать ближний вызов.
Когда Турбо Ассемблер обнаруживает, что test2 является на самом деле дальней процедурой, он определяет, что для корректной генерации вызова требуется второй проход. Если вы разрешаете нес- колько проходов (с помощью параметра-переключателя командной строки /m), то можно сделать второй проход. Если вы не разрешаете несколько проходов, то Турбо Ассемблер будет выводить ошибку 'forward reference needs override' ('опережающая ссылка требует переопределения').
Чтобы избежать такой ситуации (и уменьшить число проходов), вы можете задать в вызове расстояние процедур с опережающей ссыл- кой, как NEAR PTR и FAR PTR.
. . . test1 PROC NEAR mov AX,10 CALL FAR PTR test2 RET test1 ENDP . . .
В предыдущем примере Турбо Ассемблеру сообщается, что нужно использовать дальний вызов, поэтому не возникает необходимость в нескольких проходах.

Описание процедуры методa


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

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

Что касается всех других аспектов, то вы можете писать про- цедуры методов на любом известном вам языке или интерфейсе, хотя обычно используются соглашения по вызову C++ или Паскаля. Аргу- менты процедур также выбираются по вашему усмотрению. Обычно не- обходимым является один аргумент - указатель на экземпляр объек- та. Некоторые процедуры методов могут потребовать дополнительных параметров. Например, инициализация метода для объекта списка требует просто указатель на объект списка, в то время как метод включения в список требует указатель на список, указатель на но- вый узел для вставки и указатель на узел, включаемый после него.

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

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


Описание процедур статических и виртуальных методов в точ- ности совпадает с описанием любой другой процедуры, но имеется следующее исключение: если для виртуального метода вы опускаете имя процедуры, то в таблице виртуальных методов создается пустая неинициализированная ячейка, и Турбо Ассемблер не выводит вам ни- каких предупреждений. Если метод не является виртуальным, то про- пуск имени процедуры является ошибкой, поскольку не виртуальные методы не включаются в таблицу.
Приведем пример процедуры метода:
; Построение объекта связанного списка. ; Это метод-"конструктор". ; Этот метод должен быть статическим. ; При возврате DX:AX указывают на объект связанного списка, ; в противном случае это ноль. ; Объект выделяется, но пока не инициализируется. list_construct PROC PASCAL FAR USES ds ; -- Выделение объекта связанного списка -- ;; << выполнение выделения >> ret ENDP

Описание сложного адресного подтипа


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

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:

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.


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

Необязательное значение "счетчик_2" определяет, сколько эле- ментов определяет эта общая переменная. Если вы не задаете это поле, то подразумевается значение 1. Полный объем памяти, выделя- емой для общей переменной, равен произведению значения "счет- чик_2" на длину, заданную полем "тип", и на значение "счетчик_1".

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

Приведем пример использования директивы COMM:

COMM buffer:BYTE:512 ; во время компоновки выделяет- ; ся 512 байт COMM abc[41]:WORD:10 ; на этапе компоновки выделяет- ; ся память 820 байт (10 эле- ; ментов из 41 слова каждый)

COMM FAR abc[41]:WORD:10 ; на этапе компоновки выделяет- ; ся 10 элементов из 82 байт ; (2 байта * 41 элемент)


Определение общих сегментов и групп


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