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

MODEL можно за- давать USE32

(32-битовые флаги без директивы .386 не допускаются)

Это средство расширено. Теперь в операторе . MODEL можно за- давать USE32 и LARGESTACK. Ранее это было сообщение "USE32 not allowed without .386").

Адресные подтипы данных

Подтипы идентификаторов описывают идентификатор, представля- ющий адрес байта, слова и т.д. Простые адресные подтипы, которые предусмотрены в Турбо Ассемблере, приведены в Таблице 5.5.
Адресные подтипы Таблица 5.5 --------------------T-------------------------------------------¬ ¦ Выражение типа ¦ Значение ¦ +-------------------+-------------------------------------------+ ¦ UNKNOWN ¦ Неизвестный или неопределенный адресный ¦ ¦ ¦ подтип. ¦ ¦ ¦ ¦ ¦ BYTE ¦ Адрес, описывающий байт. ¦ ¦ ¦ ¦ ¦ WORD ¦ Адрес, описывающий слово. ¦ ¦ ¦ ¦ ¦ DWORD ¦ Адрес, описывающий 4-байтовую величину. ¦ ¦ ¦ ¦ ¦ PWORD или FWORD ¦ Адрес, описывающий 6-байтовую величину. ¦ ¦ ¦ ¦ ¦ QWORD ¦ Адрес, описывающий 8-байтовую величину. ¦ ¦ ¦ ¦ ¦ TBYTE ¦ Адрес, описывающий 10-байтовую величину. ¦ ¦ ¦ ¦ ¦ SHORT ¦ Адрес, описывающий короткий адрес метки/ ¦ ¦ ¦ процедуры. ¦ ¦ ¦ ¦ ¦ NEAR ¦ Адрес, описывающий ближний адрес метки/ ¦ ¦ ¦ процедуры. ¦ ¦ ¦ ¦ ¦ FAR ¦ Адрес, описывающий дальний адрес метки/ ¦ ¦ ¦ процедуры. ¦ ¦ ¦ ¦ ¦ PROC ¦ Адрес, описывающий ближний или дальний ¦ ¦ ¦ адрес метки/процедуры, в зависимости от ¦ ¦ ¦ текущей модели. ¦ ¦ ¦ ¦ ¦ DATAPTR ¦ Адрес, описывающий слово, двойное слово ¦ ¦ ¦ или величину pword, в зависимости от те- ¦ ¦ ¦ кущей выбранной модели. ¦ ¦ ¦ ¦ ¦ CODEPTR ¦ Адрес, описывающий слово, двойное слово ¦ ¦ ¦ или величину pword, в зависимости от те- ¦ ¦ ¦ кущей выбранной модели. ¦ ¦ ¦ ¦ ¦ имя структуры/ ¦ Адрес, описывающий экземпляр названной ¦ ¦ объединения ¦ структуры или объединения. ¦ ¦ ¦ ¦ ¦ имя таблицы ¦ Адрес, описывающий экземпляр указанной ¦ ¦ ¦ таблицы. ¦ ¦ ¦ ¦ ¦ имя записи ¦ Адрес, описывающий экземпляр указанной ¦ ¦ ¦ записи (байт, слово или двойное слово). ¦ ¦ ¦ ¦ ¦ имя перечисления ¦ Адрес, описывающий экземпляр перечислимо- ¦ ¦ ¦ го типа данных. ¦ ¦ ¦ ¦ ¦ имя типа ¦ Адрес, описывающий экземпляр указанного ¦ ¦ ¦ типа. ¦ ¦ ¦ ¦ ¦ TYPE выражение ¦ Адрес, описывающий элемент, подтип кото- ¦ ¦ ¦ рого является адресом подтипа выражения ¦ ¦ ¦ (только для режима Ideal). ¦ L-------------------+--------------------------------------------

Argument needs type override

(Требуется явно указать тип операнда)
Требуется явно указать размер, или тип, выражения, т.к. он не может быть определен из контекста. Например, ошибочной являет- ся следующая команда:
mov [bx],1
Ошибки такого рода обычно корректируются с помощью операции PTR, позволяющей установить размер операнда:
mov WORD PTR[bx],1

Argument to operation or instruction has illegal size

(Операнд операции или команды имеет недопустимый размер)
В операции указан операнд, имеющий тип, недопустимый для данной операции. Например:
Q LABEL QWORD QNOT = NOT Q ; операнд операции отрицания не может ; иметь тип QWORD

Arithmetic overflow

(Арифметическое переполнение)
Потеря значащих цифр при вычислении значения выражения. Нап- ример:
X = 20000h * 20000h ; результат занимает более 32 бит
Точность всех арифметических операций - 32 бита.

Ассемблирование вашей первой программы

После того, как вы сохранили файл HELLO.ASM, вы захотите за- пустить программу. Однако, перед тем, как вы сможете ее запус- тить, потребуется преобразовать программу в выполняемый вид. Как показано на Рис. 1.1, где изображен полный цикл создания програм- мы (редактирование, ассемблирование, компоновка и выполнение), это потребует двух дополнительных шагов - ассемблирования и ком- поновки.
На этапе ассемблирования ваш исходный код (текст программы) превращается в промежуточную форму, которая называется объектным модулем, а на этапе компоновки один или несколько модулей комби- нируются в выполняемую программу. Ассемблирование и компоновку вы можете выполнять с помощью командной строки.
Для ассемблирования файла HELLO.ASM наберите:
TASM hello
Создание новой программы ¦ --------------------------------¦ ¦ ¦ ¦ Редактирование ¦ ¦ ¦ ¦ -----------------------------------------------¬ ¦ ¦ Исходный файл Ассемблера HELLO.ASM ¦ ¦ L---------------------T------------------------- ¦ ¦ ¦ Ассемблирование ¦ ¦ ¦ ¦ ----------------------------------------------¬ ¦ ¦ Объектный файл HELLO.OBJ ¦ ¦ L---------------------T------------------------ ¦ ¦ ¦ Компоновка ¦ ¦ ¦ ¦ ----------------------------------------------¬ ¦ ¦ Выполняемый файл HELLO.EXE ¦ ¦ L---------------------T------------------------ ¦ ¦ ¦ Выполнение ¦ -----------------------¬ ¦ L---+ Если нужны изменения ¦----- L-----------------------
Рис. 1.1 Редактирование, ассемблирование, компоновка и вы- полнение.
и нажмите клавишу Enter. Если вы не задали другое имя, файл HELLO.ASM будет ассемблирован в файл HELLO.OBJ. (Заметим, что расширение имени файла вводить не требуется. Турбо Ассемблер под- разумевает в этом случае, что файл имеет расширение .ASM.) На эк- ране вы увидите следующее:
Turbo Assembler Version 3.0 Copyright (C) 1988,1991 (1) by Borland International Inc. Assembling file: HELLO.ASM (2) Error messages: None (3) Warning messages: None (4) Passes: 1 (5) Remaining memory: 266K (6)
1 - Турбо Ассемблер, версия 3.0; авторские права фирмы Borland, 1991 г.; 2 - ассемблирован файл HELLO.ASM; 3 - сообщения об ошибках: нет; 4 - предупреждающие сообщения: нет; 5 - число проходов: 1; 6 - остается памяти: 266К
Если вы введете файл HELLO.ASM в точности так, как показано, то вы не получите никаких предупреждающих сообщений или сообщений об ошибках. Если вы получаете такие сообщения, они появляются на экране наряду с номерами строк, указывающими строки, где содер- жатся ошибки. При получении сообщений об ошибках проверьте исход- ный код (текст) программы и убедитесь, что он выглядит точно так, как исходный код в нашем примере, а затем снова ассемблируйте программу.

ASSUME must be segment register

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

Атрибут доступа к сегменту

В защищенном режиме для любого сегмента вы можете управлять доступом к определенным операциям с памятью, запрещая их. (Заме- тим, что данное средство поддерживается в настоящее время только компоновщиком Phar Lap. Если требуется использовать атрибут дос- тупа к сегменту, вы должны компилировать совместимый с ним объек- тный код с помощью параметра командной строки /op.) Атрибут дос- тупа к сегменту сообщает компоновщику, что к сегменту нужно применить специальные ограничения доступа.
Допустимые значения данного атрибута приведены в следующей таблице:
Значения атрибута доступа к сегменту Таблица 7.9 ----------------T-----------------------------------------------¬ ¦ Атрибут ¦ Значение ¦ +---------------+-----------------------------------------------+ ¦ EXECONLY ¦ Сегмент доступен только для выполнения. ¦ ¦ ¦ ¦ ¦ EXECREAD ¦ Сегмент доступен только для чтения и выполне- ¦ ¦ ¦ ния. ¦ ¦ ¦ ¦ ¦ READONLY ¦ Сегмент доступен только для чтения. ¦ ¦ ¦ ¦ ¦ READWRITE ¦ Сегмент доступен только для чтения и записи. ¦ L---------------+------------------------------------------------
Если вы выбираете один из этих атрибутов или используете USE32, компоновщик Phar Lap предполагает, что сегмент предназна- чен для выполнения в защищенном режиме. Если вы выбираете USE32, но не задаете одно из этих значений, Турбо Ассемблер предполагает использование атрибута READONLY.

Атрибут класса сегмента

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

Атрибут комбинирования сегментов

Атрибут комбинирования сегментов сообщает компоновщику, как нужно комбинировать сегменты различных модулей, имеющих одно и то же имя. Допустимые значения атрибута комбинирования сегмента пе- речисляются в следующем списке. Заметим, что если вы не указывае- те этот атрибут, Турбо Ассемблер предполагает PRIVATE.
Атрибут комбинирования сегмента Таблица 7.6 --------------------T-------------------------------------------¬ ¦ Атрибут ¦ Значение ¦ +-------------------+-------------------------------------------+ ¦ PRIVATE ¦ Сегмент не будет комбинироваться с други- ¦ ¦ ¦ ми сегментами с тем же именем вне данного ¦ ¦ ¦ модуля. Будет выполняться конкатенация ¦ ¦ ¦ сегмента с сегментами с тем же именем вне ¦ ¦ ¦ данного модуля для образования одного ¦ ¦ ¦ непрерывного сегмента. ¦ ¦ ¦ ¦ +-------------------+-------------------------------------------+ ¦ MEMORY ¦ То же, что PUBLIC. Будет выполняться кон- ¦ ¦ ¦ катенация сегмента с другими сегментами ¦ ¦ ¦ с тем же именем вне данного модуля для ¦ ¦ ¦ образования одного непрерывного сегмента, ¦ ¦ ¦ используемого как сегмент стека. Компо- ¦ ¦ ¦ новщик инициализирует регистры SS SP на- ¦ ¦ ¦ чальными значениями, так что они указы- ¦ ¦ ¦ вают на конец данного сегмента. ¦ ¦ ¦ ¦ +-------------------+-------------------------------------------+ ¦ COMMON ¦ Располагает данный сегмент и все другие ¦ ¦ ¦ сегменты с тем же именем по одному адре- ¦ ¦ ¦ су. Все сегменты с данным именем будут ¦ ¦ ¦ перекрываться и совместно использовать ¦ ¦ ¦ общую память. Размер полученного в ре- ¦ ¦ ¦ зультате сегмента будет равен размеру са- ¦ ¦ ¦ мого большого сегмента модуля. ¦ ¦ ¦ ¦ +-------------------+-------------------------------------------+ ¦ VIRTUAL ¦ Определяет специальный вид сегмента, ко- ¦ ¦ ¦ торый должен описываться внутри охватыва- ¦ ¦ ¦ ющего сегмента. Компоновщик интерпретиру- ¦ ¦ ¦ ет его как общую область и присоединяет ¦ ¦ ¦ его к охватывающему сегменту. Виртуальный ¦ ¦ ¦ сегмент наследует свои атрибуты из охва- ¦ ¦ ¦ тывающего сегмента. Директива ASSUME ¦ ¦ ¦ рассматривает виртуальный сегмент как ¦ ¦ ¦ часть порождающего сегмента. Во всех дру- ¦ ¦ ¦ гих отношениях виртуальный сегмент предс- ¦ ¦ ¦ тавляет собой общую область памяти, кото- ¦ ¦ ¦ рая используется разными сегментами. Это ¦ ¦ ¦ позволяет организовать совместное исполь- ¦ ¦ ¦ зование статических данных, которые бе- ¦ ¦ ¦ рутся различными модулями из включаемых ¦ ¦ ¦ файлов. ¦ ¦ ¦ ¦ +-------------------+-------------------------------------------+ ¦ AT xxx ¦ Располагает сегмент по абсолютному адресу ¦ ¦ ¦ параграфа. Адрес задается параметром xxx. ¦ ¦ ¦ Компоновщик для сегмента AT не порождает ¦ ¦ ¦ никаких данных или кода. Используйте ди- ¦ ¦ ¦ рективу AT для организации доступа по ¦ ¦ ¦ идентификатору к фиксированным адресам ¦ ¦ ¦ памяти (например, экран дисплея или об- ¦ ¦ ¦ ласти ПЗУ). ¦ L-------------------+--------------------------------------------

Атрибут размера сегмента

Если текущим выбранным процессором является процессор 80386, то сегменты могут быть 16- или 32-разрядными. Размер атри- бута сегмента сообщает компоновщику, какой размер вы хотите за- дать для конкретного сегмента. Допустимые значения атрибута при- ведены в следующей таблице:
Значения атрибута размера сегмента Таблица 7.8 ----------------T-----------------------------------------------¬ ¦ Атрибут ¦ Значение ¦ +---------------+-----------------------------------------------+ ¦ USE16 ¦ Сегмент будет 16-разрядным. Такой сегмент мо- ¦ ¦ ¦ жет содержать до 64К кода или данных. ¦ ¦ ¦ ¦ ¦ USE32 ¦ Сегмент будет 32-разрядным. Такой сегмент мо- ¦ ¦ ¦ жет содержать до 4 гигабайт кода или данных. ¦ L---------------+------------------------------------------------
Если в режиме MASM вы выбираете процессор 80386, то Турбо Ассемблер предполагает использование USE32. В режиме Ideal Турбо Ассемблер по умолчанию предполагает использование USE32.

Атрибут выравнивания сегмента

Атрибут выравнивания сегмента сообщает компоновщику, что нужно обеспечить размещение начала сегмента на заданной границе. Это важно, поскольку при правильном выравнивании доступ к данным в процессорах 80х86 выполняется быстрее. Допустимые значения это- го атрибута приведены в следующей таблице:
Атрибут выравнивания сегмента Таблица 7.7 -----------------------T----------------------------------------¬ ¦ Атрибут ¦ Значение ¦ +----------------------+----------------------------------------+ ¦ BYTE ¦ Выравнивание не выполняется. Сегмент ¦ ¦ ¦ начинается с границы следующего байта. ¦ ¦ ¦ ¦ ¦ WORD ¦ Начинает сегмент на границе следующего ¦ ¦ ¦ слова. ¦ ¦ ¦ ¦ ¦ DWORD ¦ Начинает сегмент на границе следующего ¦ ¦ ¦ двойного слова. ¦ ¦ ¦ ¦ ¦ PARA ¦ Начинает сегмент на границе следующего ¦ ¦ ¦ параграфа (выравнивание на 16 байт). ¦ ¦ ¦ ¦ ¦ PAGE ¦ Начинает сегмент на границе следующей ¦ ¦ ¦ страницы (выравнивание на 256 байт). ¦ ¦ ¦ ¦ ¦ MEMPAGE ¦ Начинает сегмент на границе следующей ¦ ¦ ¦ страницы памяти (выравнивание на 4 ки- ¦ ¦ ¦ лобайта). ¦ L----------------------+-----------------------------------------
Если вы не задаете тип выравнивания, Турбо Ассемблер подра- зумевает PARA.

Bad keyword in SEGMENT statement

(Неверное ключевое слово в операторе SEGMENT)
Один из параметров директивы SEGMENT: тип выравнивания, тип объединения или тип сегмента имеет недопустимое значение. Напри- мер:
DATA SEGMENT PAFA PUBLIC ; вместо PARA указано PAFA

Bad switch

(Неверный параметр-переключатель командной строки)
В командной строке указан неверный параметр. См. Главу 2, где дается подробное описание параметров командной строки.

Безусловные директивы генерации сообщений об ошибке

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

Can't add relative quantities

(Нельзя складывать относительные адреса)
Выражение содержит операцию сложения двух адресов, что явля- ется бессмысленной операцией. Например:
ABC DB ? DEF = ABC + ABC ; ошибка: нельзя складывать ; два относительные адреса
Можно вычитать относительные адреса. Можно добавить констан- ту к относительному адресу, например:
XYZ DB 5 DUP(0) XYZEND EQU $ XYZLEN = SYZEND - XYZ ; совершенно верно XYZ2 = XYZ + 2 ; тоже верно

Can't address with currently ASSUMEd segment registers

(Невозможна адресация из текущих, установленных директивой ASSUME, сегментных регистров)
В выражении содержится ссылка на переменную, для доступа к которой не специфицирован сегментный регистр. Например:
DSEG SEGMENT ASSUME ds:DSEG mov si,MPTR ; не определен сегментный регистр, который ; обеспечил бы доступ к сегменту XSEG DSEG ENDS XSEG SEGMENT MPTR DW ? XSEG ENDS

Can't convert to pointer

(Невозможно преобразование в указатель)
Часть выражения не может быть преобразована в указатель на память, например, с помощью операции PTR:
mov cl,[BYTE PTR al] ; AL нельзя преобразовать ; в указатель

Can't emulate 8087 instruction

(Невозможна эмуляция команд сопроцессора 8087)
В Турбо Ассемблере параметром командной строки /E либо с по- мощью директивы EMUL установлен режим генерации эмулированных ко- манд арифметики с плавающей точкой, однако текущая команда не мо- жет быть эмулирована. Например:
EMUL FNSAVE [WPTR] ; эта команда не может быть эмулирована
Некоторые команды не поддерживаются эмуляторами арифметики с плавающей точкой. Это команды FNSAVE, FNSTCW, FNSTENV и FNSTSW.

Can't find @file __

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

Can't make variable public

(Переменная не может быть объявлена как PUBLIC)
Переменная была уже ранее объявлена таким образом, что уже не может быть определена как общая (PUBLIC). Например:
EXTRN ABC:NEAR PUBLIC ABC ; ошибка: ABC уже ранее объявлена ; с атрибутом EXTRN

Can't override ES segment

(Нельзя переопределить сегмент ES)
В текущем операторе указан регистр, использование которого в данной команде недопустимо. Например:
STOS DS:BYTE PTR[di]
В команде STOS для определения целевого адреса допускается использовать только регистр ES.

Can't subtract dissimilar relative quantities

(Недопустимое вычитание относительных адресов)
Выражение содержит операцию вычитания двух адресов, которая для данных адресов является недопустимой. Данное сообщение выда- ется, например, в том случае, если адреса находятся в разных сег- ментах. Например:
SEG1 SEGMENT A: SEG1 ENDS SEG2 SEGMENT B: mov ax,B-A ; недопустимо, поскольку A и В находятся ; в разных сегментах SEG2 ENDS

Can't use macro name in expression

(Недопустимо использование имени макрокоманды в качестве операнда выражения)
Имя макрокоманды указано в качестве операнда выражения. Нап- ример:
MyMac MACRO ENDM mov ax,MyMac ; ошибка!

Can't use this outside macro

(Использование данного оператора недопустимо вне макроопре- деления)
Вне макроопределения указана директива, которую допускается использовать только внутри макроопределений. К таким директивам относятся, например, ENDM и EXITM. Например:
DATA SEGMENT ENDM ; ошибка: вне макроопределения недопустимо

Числовые константы

Числовые константы в Турбо Ассемблере всегда начинаются с цифры (0-9) и содержат произвольное число алфавитно-цифровых символов. Фактическое значение константы зависит от основания, которое вы выбираете для ее интерпретации. В Турбо Ассемблере можно использовать двоичное, восьмеричное, десятичное или шест- надцатиричное основание, что показано в приведенной ниже таблице:
Основания Таблица 5.1 ------------------------T---------------------------------------¬ ¦ Основание ¦ Допустимые цифры ¦ +-----------------------+---------------------------------------+ ¦двоичное ¦ 0 1 ¦ ¦восьмеричное ¦ 0 1 2 3 4 5 6 7 ¦ ¦десятичное ¦ 0 1 2 3 4 5 6 7 8 9 ¦ ¦шестнадцатиричное ¦ 0 1 2 3 4 5 6 7 8 9 A B C D E F ¦ L-----------------------+----------------------------------------
Заметим, что в шестнадцатиричных константах вы можете ис- пользовать буквы как в верхнем, так и в нижнем регистре.
Турбо Ассемблер определяет основание числовой константы, проверяя сначала последний ее символ. Символы, используемые для задания основания при интерпретации константы, приведены в следу- ющей таблице:
Символы, определяющие основания Таблица 5.2 ---------------------T------------------------------------------¬ ¦ Символ ¦ Основание ¦ +--------------------+------------------------------------------+ ¦ B ¦ двоичное ¦ ¦ O ¦ восьмеричное ¦ ¦ Q ¦ восьмеричное ¦ ¦ D ¦ десятичное ¦ ¦ H ¦ шестнадцатиричное ¦ L--------------------+-------------------------------------------
Для задания основания числа можно использовать символы как верхнего, так и нижнего регистра. Последним символом числовой константы должно быть одно из этих значений. Если последним сим- волом числовой константы не является один из этих символов, Тур- бо Ассемблер будет для интерпретации константы использовать теку- щее назначенное по умолчанию основание. Доступные числовые конс- танты и их значения приведены в следующей таблице:
Числовые константы Таблица 5.3 -----------------------T----------------------------------------¬ ¦Числовая константа ¦ Значение ¦ +----------------------+----------------------------------------+ ¦ 77d ¦ 77 десятичное ¦ ¦ 77h ¦ 77 шестнадцатиричное ¦ ¦ ffffh ¦ недопустимо, не начинается с цифры ¦ ¦ 0ffffh ¦ FFFF шестнадцатиричное ¦ ¦ 88 ¦ интерпретация зависит от текущего ис- ¦ ¦ ¦ пользуемого по умолчанию основания ¦ L----------------------+-----------------------------------------

Что происходит?

Теперь, когда вы получили и выполнили программу HELLO.ASM, давайте вернемся назад и рассмотрим подробно, что происходит с момента ввода текста программы до ее выполнения.
Когда вы ассемблируете файл HELLO.ASM, Турбо Ассемблер прев- ращает текст инструкций в этом файле в их двоичный эквивалент в объектном файле HELLO.OBJ. Этот файл является промежуточным фай- лом (промежуточным звеном в процессе перехода от текстового к вы- полняемому файлу). Файл HELLO.OBJ содержит всю информацию, необ- ходимую для создания выполняемого кода из инструкций, содержащих- ся в файле HELLO.ASM, но она записана в виде, который позволяет комбинировать ее с другими объектными файлами для создания одной программы.
При компоновке файла HELLO.OBJ TLINK преобразует его в вы- полняемый файл HELLO.EXE, который вы запускаете, введя hello в ответ на подсказку DOS.
Теперь введите:
dir hello.*
При этом будет выведен список файлов HELLO на диске. Это будут файлы HELLO.ASM, HELLO.OBJ, HELLO.EXE и HELLO.MAP.

Что такое объект?

Объект состоит из структуры данных и связанных с ней проце- дур (которые называются методами), которые работают с данными, записанными в экземплярах структуры данных.
Объект может наследовать характеристики порождающего объек- та. Это означает, что структура данных нового объекта включает структуру данных порождающего объекта, а также новые данные. Кро- ме того, новый объект может вызывать все процедуры порождающего объекта, а также те процедуры методов, которые в нем описываются.
Примечание: Для объектно-ориентированного программиро- вания мы настоятельно рекомендуем вам использовать режим Ideal Турбо Ассемблера, поскольку область действия иденти- фикаторов в MASM является глобальной, и вы не сможете раз- личить различные расположения показанных методов.
Объект, не имеющий наследования, называется базовым объек- том. Объект, наследующий характеристики других объектов, называ- ется порожденным или производным объектом.
В Турбо Ассемблере определено несколько идентификаторов, ко- торые вы можете использовать при описании объектов. Они перечис- лены в следующей таблице:
Идентификаторы, определенные для объектов Таблица 4.2 -----------------------------T----------------------------------¬ ¦ Идентификатор ¦ Значение ¦ +----------------------------+----------------------------------¦ ¦ @Object ¦ Текстовая макрокоманда, содержа- ¦ ¦ ¦ щая имя текущего объекта (пос- ¦ ¦ ¦ леднего описанного объекта). ¦ ¦ ¦ ¦ ¦ <имя_объекта> ¦ Тип данных STRUC, описывающий ¦ ¦ ¦ структуру данных объекта. ¦ ¦ ¦ ¦ ¦ @Table_<имя_объекта> ¦ Тип данных TABLE, содержащий ¦ ¦ ¦ таблицу методов объекта. Это не ¦ ¦ ¦ то же самое, что экземпляр таб- ¦ ¦ ¦ лицы виртуальных методов. ¦ ¦ ¦ ¦ ¦ @TableAddr_<имя_объекта> ¦ Метка, описывающая адрес экземп- ¦ ¦ ¦ ляра таблицы виртуальных мето- ¦ ¦ ¦ дов объекта (если она есть). ¦ L----------------------------+-----------------------------------

Code or data emission to undeclared segment

(Не объявлен сегмент для кода или данных)
Оператор, генерирующий код или данные, не принадлежит ни од- ному из сегментов, объявленных директивами SEGMENT. Например:
; Первая строка файла inc bx ; ошибка: не определен сегмент END
Генерировать данные или код можно только внутри какого-либо сегмента.

Constant assumed to mean Immediate const

(Константа интерпретируется как непосредственная)
Это предупреждающе сообщение выдается для выражений типа [0]. В режиме MASM это выражение интерпретируется как непосредс- твенная константа, равная 0. Например:
mov ax,[0]; означает mov ax,0, а не mov ax,ds:[0]

Constant too large

(Слишком большая константа)
Константа имеет, вообще говоря, правильный формат, однако ее значение превышает допустимую для данного режима величину. Напри- мер, числа, большие 0ffffh, можно использовать, если только ди- рективой .386/.386P или .486/.486Р разрешены команды процессора 386 или i486.

CS not correctly assumed

(Некорректное значение в регистре CS)
Адрес назначения в командах ближнего вызова и ближнего пере- хода не может находиться в другом сегменте. Например:
SEG1 SEGMENT LAB1 LABEL NEAR SEG1 ENDS SEG2 SEGMENT JMP LAB1 ; ошибка: неверный сегментный адрес SEG2 ENDS
Такие ошибки возникают только в режиме MASM. В режиме Ideal такие переходы и вызовы интерпретируются корректно.

CS override in protected mode

(Переопределение регистра CS в защищенном режиме)
В защищенном режиме ассемблирования команд процессора 286, 386 или i486, установленном директивой Р286Р, P386P или Р486Р, в текущей команде требуется переопределение регистра CS. Например:
P286 .CODE CVAL DW ? mov CVAL,1 ; генерирует переопределение регистра CS
Это предупреждающее сообщение выдается, если в командной строке указан параметр /Р. В защищенном режиме команды, в которых переопределяется регистр CS, не будут выполняться без специальных подготовительных операций.

CS unreachable from current segment

(CS недостижим из текущего сегмента)
При определении метки кода с помощью двоеточия (:) или с по- мощью директив LABEL или PROC сегментный регистр не указывает на текущий кодовый сегмент или группу, содержащую текущий кодовый сегмент. Например:
PROG1 SEGMENT ASSUME CS:PROG2 START: ; ошибка: неверно установлен регистр CS
Такие ошибки возникают только в режиме MASM. В режиме Ideal такие переходы и вызовы обрабатываются корректно.

Declaration needs name

(В директиве объявления не указано имя)
Не указано имя идентификатора в директиве, для которой спе- цификация имени является обязательной. Например:
PROC ; ошибка: в директиве PROC указание имени обязательно ret ENDP
В директивах объявления, таких как SEGMENT, PROC или STRUC, обязательно должно быть указано имя идентификатора. В режиме MASM имя указывается перед именем директивы, а в режиме Ideal - после имени директивы.

Directive ignored in Turbo Pascal model

(В режиме TPASCAL директива игнорируется)
В модуле Ассемблера, предназначенном для интерфейса с Турбо Паскалем, используется недопустимая директива. Режим интерфейса с Турбо Паскалем специфицируется директивой .MODEL. Более подробно интерфейс с Турбо Паскалем обсуждается в Главе 19.

Directive not allowed inside structure definition

(Недопустимая директива внутри определения структуры)
Внутри блока определения структуры указана недопустимая ди- ректива. Например:
X STRUC MEM1 DB ? ORG $+4 ; ошибка: директиву ORG нельзя указывать ; внутри структуры MEM2 DW ? ENDS
При определении вложенных структур нельзя определять новые структуры на внутренних уровнях. Например:
F00 STRUC F002 STRUC ; ошибка: определена новая структура ENDS ENDS
Для того чтобы использовать одну структуру внутри другой структуры, нужно сначала определить первую структуру, а после этого во второй структуре можно указывать имя первой.

Директива ALIGN

Для выравнивания счетчика адреса на адрес, значение которо- го равно степени 2, можно использовать директиву ALIGN. Директива ALIGN имеет следующий синтаксис:
ALIGN граница
где "граница" должна быть степенью 2.
Если счетчик адреса еще не соответствует смещению, которое представляет собой произведение "границы", Турбо Ассемблер, чтобы присвоить счетчика адреса нужный адрес, вставляет в сегмент инс- трукции NOP (нет операции).
Вы не можете выполнить надежное выравнивание на границу, яв- ляющееся более строгим, чем выравнивание сегмента, в котором встречается директива ALIGN. Выравнивание сегмента задается, ког- да сегмент в первый раз начинается по директива SEGMENT.
Например, если вы определили сегмент следующим образом:
CODE SEGMENT PARA PUBLIC
затем вы можете задать ALIGN 16 (что эквивалентно PARA), но не ALIGN 32, как как это более строгое выравнивание, чем выравнива- ние, заданное в директиве SEGMENT с помощью PARA. Если выравнива- ние сегменте недостаточно строгое, то директива ALIGN генерирует предупреждающее сообщение.
Использование директивы ALIGN показано в следующем примере:
ALIGN 4 ; выравнивание на границу DWORD для 386 BignNum DD 12345678

Директива .ALPHA

Директива .ALPHA определяет упорядочивание сегментов по ал- фавиту. Данная директива сообщает Турбо Ассемблеру, что сегменты в объектном файле нужно разместить в алфавитном порядке (в соот- ветствии с именами сегментов). Она имеет синтаксис:
.ALPHA

Директива ARG

Хотя можно обращаться к параметрам через регистр BP, Турбо Ассемблер предусматривает альтернативу вычислению смещений в сте- ке и выполнению текстовых присваиваний. Это директива ARG. При использовании ее в процедуре директива ARG автоматически опреде- ляет смещения параметров относительно регистра BP. Она вычисляет также размер блока параметров и использует его в инструкции RET. Поскольку идентификаторы, создаваемые по директиве ARG, определе- ны только в соответствующей процедуре, в каждой процедуре или функции вам не требуется использовать уникальные имена парамет- ров.
Покажем, как будет выглядеть пример предыдущего раздела, если переписать его, используя директиву ARG:
CODE SEGMENT ASSUME CS:CODE MyProc PROC FAR ; procedure MyProc(i,j : integer); ; external; PUBLIC MyProc ARG j : WORD, i : WORD = RetBytes push bp ; нужно сохранить BP вызывающей ; программы mov bp,sp ; BP теперь указывает на вершину ; стека mov ax,i ; адресуемся к i через BP . . .
Директива ARG Турбо Ассемблера создает локальные идентифика- торы для параметров i и j. На время выполнения процедуры строка:
ARG j : WORD, i : WORD = RetBytes
автоматически приравнивает идентификатор i к [WORD PTR BP+6], идентификатор j к [WORD PTR BP+8], а идентификатор RetBytes - к числу 4 (размеру в байтах блока параметров). В значениях учитыва- ется и занесенное в стек значение BP, и размер адреса возврата: если бы процедура MyProc имела ближний тип, то i было бы прирав- нено к значению [BP+4], j - к [BP+6], а RetBytes также было бы равно 4 (в любом случае процедура MyProc может завершить выполне- ние с помощью инструкции RET RetBytes).
При использовании директивы ARG нужно помнить, что параметры должны перечисляться в обратном порядке. Последний параметр про- цедуры или функции Турбо Паскаля нужно размещать в директиве ARG первым и наоборот.
Относительно использования директивы ARG с Турбо Паскалем можно сделать еще одно замечание. В отличие от других языков, Турбо Паскаль всегда заносит в стек параметр-значение размером в байт, как 16-битовое слово. При этом сообщить Турбо Ассемблеру о дополнительном байте должны вы. Предположим, например, что вы на- писали функцию, описание которой в Паскале выглядит следующим об- разом:
function MyProc(i, j : char) : string; external;
Директива ARG для этой функции должна была бы выглядеть так:
ARG j: BYTE: 2, i:BYTE: 2 = RetBytes RETURN result: DWORD
Здесь 2 после каждого аргумента необходимо указывать для то- го, чтобы сообщить Ассемблеру, что каждый идентификатор заносится в стек, как массив из 2 байт (где, в данном случае, младший байт каждой пары содержит полезную информацию).
В функции, возвращающей строковое значение (как данная функ- ция), параметр RETURNS в директиве ARG позволяет вам определить переменную, приравненную к тому месту в стеке, которое указывает на временный результат функции. Переменная в RETURNS на размер (в байтах) блока параметров.

Директива ASSUME

Если вы хотите получить доступ к данным сегмента, сегментный регистр должен загружаться корректным значением сегмента. Часто это нужно делать вам самим. Например, для загрузки в регистр DS адреса текущего сегмента данных дальнего типа вы можете использо- вать команды:
MOV AX,@fardata MOV DS,AX
Когда программа загружает в сегментный регистр значение сег- мента, вы можете использовать этот сегментный регистр для доступа к данным в сегменте. Это быстро утомляет, и вы начинаете забывать каждый раз при обработке данных в памяти задавать сегментный ре- гистр (или у вас недостаточно практики в программировании).
Чтобы указать Турбо Ассемблеру, что нужно связать сегментный регистр с именем сегмента или группы, используйте директиву ASSUME. Это позволяет Турбо Ассемблеру быть "достаточно проница- тельным" и использовать при доступе к данным конкретный сегмент.
Фактически, Турбо Ассемблер использует также информацию о связи между сегментным регистром и именем сегмента также и для других целей: в режиме MASM значение, которое подразумевается для регистра CS, используется для определения сегмента или группы, к которому принадлежит метка. Таким образом, регистр CS должен кор- ректно задаваться в директиве ASSUME, в противном случае Турбо Ассемблер при каждом определении метки или процедуры будет сооб- щать об ошибке.
Директива ASSUME имеет следующий синтаксис:
ASSUME сегм_регистр : выражение [, сегм_регистр : выражение]
или ASSUME nothing
где "сегм_регистр" - это один из регистров CS, DS, ES или SS. Ес- ли вы задаете процессор 80386 или 80486, то можете использовать регистры FS и GS. "Выражение" может быть любым выражением, при вычислении которого получается имя сегмента или группы. В против- ном случае может использоваться ключевое слово NOTHING. Это клю- чевое слово отменяет связь между сегментным регистром и любым сегментом или именем группы.
Директива ASSUME NOTHING отменяет связь между всеми сегмент- ными регистрами и сегментом или именем группы.
Вы можете использовать директиву ASSUME при модификации сег- ментного регистра или в начале процедуры для задания в этой точке предположений о сегментных регистрах. На практике ASSUME исполь- зуется обычно в начале модуля и иногда внутри него. Если вы ис- пользуете оператор MODEL, то Турбо Ассемблер назначает директиву ASSUME по умолчанию.
Если вы не задаете в директиве ASSUME значение, то ранее за- данное в ней значение не изменяется.
Например, в следующем фрагменте программы показано, как мож- но загрузить текущий инициализированный сегмент данных дальнего типа в регистр DS, обратиться через этот регистр к памяти и восс- тановить регистр DS в значение сегмента данных:
MOV AX,@fardata MOV DS,AX ASSUME DS:@fardata: MOV BX,<переменная_данных_дальнего_типа> MOV AX,@data MOV DS,AX ASSUME DS:@data

Директива CATSTR

Директива CATSTR определяет новую текстовую макрокоманду пу- тем конкатенации строк. Она имеет следующий синтаксис:
имя CATSTR строка[,строка].
Директива CATSTR выполняет конкатенацию слева-направо. Турбо Ассемблер создает новую текстовую макрокоманду с именем "имя".

Директива COMMENT

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

Директива DOSSEG: упорядочивание сегментов в порядке DOS

Обычно компоновщик упорядочивает сегменты в последовательном порядке - в том порядке, в котором он их обрабатывает при генера- ции программы. Если вы включаете в любой модуль программу дирек- тиву DOSSEG, то это указывает компоновщику, что вместо этого упорядочивания нужно использовать порядок сегментов, принятый в DOS. Компоновщик при этом выполняет в получаемой программе следу- ющее упорядочивание:
- сначала идут сегменты с именем класса CODE (обычно сегмен- ты кода);
- затем следуют сегменты, не имеющие имени класса CODE и не являющиеся частью DGROUP;
- сегменты, являющиеся частью DGROUP в следующем порядке:
1. сегменты, не являющиеся классами BSS и STACK (обыч- но неинициализированные данные);
2. сегменты класса BSS (обычно инициализированные дан- ные);
3. сегменты класса STACK (область стека).
Примечание: Не используйте директиву DOSSEG, в прог- раммах, не являющихся автономными программами на Ассембле- ре.
Сегменты в группе DGROUP располагаются в том порядке, в ко- тором они определены в исходных модулях. Приведем синтаксис DOSSEG:
DOSSEG

Директива END

Используйте директиву END, чтобы отметить конец исходного файла. При этом используется следующий синтаксис:
END начальный_адрес:
где "начальный_адрес" - это необязательный идентификатор или вы- ражение, определяющий адрес в программе, с которого вы хотите на- чать выполнение. Если ваша программа скомпонована из нескольких исходных файлов, начальный адрес может задаваться только в одном из них. Этот адрес может представлять собой адрес в модуле. Он может быть также внешним идентификатором, определенным в другом модуле, описанном по директиве EXTRN.
Любой текст, указанный в исходном файле после директивы END, Турбо Ассемблер игнорирует.
Пример:
.MODEL small .CODE ; тело программы END START ; точка входа программы "START" THIS LINE IS IGNORED ; эта строка игнорируется SO IS THIS ONE ; эта строка тоже

Директива ENDS

Вы можете использовать директиву ENDS для закрытия сегмента, после чего данные в него больше включаться не будут. Директиву ENDS следует использовать для закрытия любого сегмента, открытого по директиве SEGMENT. Сегменты, открытые с помощью упрощенных ди- ректив определения сегментов, не требуют директивы ENDS.
Директива ENDS имеет следующий синтаксис:
ENDS [имя]
В режиме MASM вы можете использовать следующий синтаксис:
имя ENDS
где "имя" задает имя сегмента, который должен быть закрыт. Если имя не согласуется с именем текущего открытого сегмента, Турбо Ассемблер будет выводить сообщение об ошибке. Если имя не задает- ся, Турбо Ассемблер подразумевает текущий сегмент.

Директива EXITCODE

Директива EXITCODE используется для генерации кода заверше- ния, соответствующего текущей операционной системе. Вы можете использовать ее в модуле несколько раз (для каждой точки входа). Эта директива имеет следующий синтаксис:
EXITCODE [возвращаемое_значение]
В режиме MASM вы можете использовать следующий синтаксис:
.EXIT [возвращаемое_значение]
Необязательное "возвращаемое_значение" описывает число, ко- торое должно возвращаться в операционную систему. Если вы не за- даете возвращаемое значение, Турбо Ассемблер предполагает, что это значение содержится в регистре AX.

Директива EXITM

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

Директива EXTRN

Модуль Турбо Ассемблера может обращаться к любой процедуре, функции, переменной или типизованной константе Турбо Паскаля, ко- торая описывается на самом внешнем уровне программы или модуля, с которым она компонуется. (Заметим, что это включает в себя пере- менные, описанные после директивы компилятора {$L} и внешние опи- сания, связанные с данным модулем.) Метки и обычные константы Турбо Паскаля языку Ассемблера недоступны.
Примечание: Эти включает в себя переменные, указанные после директивы компилятора $L и описаниях external, свя- занных с данным модулем.
Предположим, в вашем программе Турбо Паскаля описываются следующие глобальные переменные:
var a : byte; b : word; c : shortint; d : integer; e : real; f : single; g : double; h : extended; i : comp; j : pointer;
В программе на языке Ассемблера вы можете получить доступ ко всем этим переменным с помощью описаний EXTRN:
EXTRN A : BYTE ; 1 байт EXTRN B : WORD ; 2 байта EXTRN C : BYTE ; в Ассемблере значения со знаком и ; без знака интерпретируются одинаково EXTRN D : WORD ; то же самое EXTRN E : FWORD ; 6-байтовое действительное значение ; (обрабатывается программно) EXTRN F : DWORD ; 4-байтовое значение с плавающей ; точкой в формате IEEE EXTRN G : QWORD ; 8-байтовое значение с плавающей ; точкой (двойной точности) в ; формате IEEE EXTRN H : TBYTE ; 10-байтовое значение с плавающей ; точкой во временном формате EXTRN I : QWORD ; 8-байтовое целое со знаком в ; формате IEEE (сопроцессор 8087) EXTRN J : DWORD ; указатель Турбо Паскаля
Аналогичным образом можно получить доступ к процедурам и функциям Турбо Паскаля, включая библиотечные. Предположим, у вас имеется модуль Турбо Паскаля, который выглядит следующим образом:
unit Sample; { Пример модуля, в котором определяется нескольку процедур Паскаля, вызываемых из процедуры на языке Ассемблера }
interface
procedure TestSample;
procedure PublicProc; { для обращения извне должна быть дальнего типа } inplementation
var A : word;
procedure AsmProc; external; {$L ASMPROC.OBJ}

procedure PublicProc; begin { PublicProc } Writeln('В PublicProc'); end { PublicProc }

procedure NearProc; { должна быть ближнего типа } begin { NearProc } Writeln('B NearProc'); end; { NearProc }

{$F+} procedure FarProc { должна иметь дальний тип согласно директиве компилятора } begin { FarProc } Writeln('B FarProc'); end { FarProc }

{$F-}

procedure TestSample; begin { TestSample } Writeln('B TestSample'); A := 10; Writeln('Значение A перед ASMPROC = ',A); AsmProc; Writeln('Значение A после ASMPROC = ',A); end { TestSample };

end.

Процедура AsmProc вызывает процедуры PublicProc, NearProc или FarProc, используя директиву EXTRN следующим образом:

DATA SEGMENT WORD PUBLIC ASSUME DS:DATA EXTRN A:WORD ; переменная из модуля DATA ENDS

CODE SEGMENT BYTE PUBLIC ASSUME CS:CODE EXTRN PublicProc : FAR ; дальняя процедура ; (экспортируется модулем) EXTRN NearProc : NEAR ; ближняя процедура ; (локальная для модуля) EXTRN FarProc : FAR ; дальняя процедура ; (локальна, но задана, ; как дальняя) AsmProc PROC NEAR PUBLIC AsmProc CALL FAR PTR PublicProc CALL NearProc CALL FAR PTR FarProc mov cx,ds:A ; взять переменную из ; модуля sub cx,2 ; изменить ее mov ds:A,cx ; записать ее обратно RET AsmProc ENDP CODE ENDS END

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

program TSample; uses Sample; begin TestSample; end.

Чтобы сформировать пример программы с помощью компилятора, работающего в режиме командной строки, и Ассемблера, используйте следующие команды (или командный файл):

TASM ASMPROC TPC /B SAMPLE TSAMPLE

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

Директива GOTO и макроидентификаторы перехода

Использование директивы GOTO и макроидентификаторов перехода позволяют вам управлять последовательностью расширения строк мак- рокоманды. Вы можете поместить цель перехода в любом месте тела макрокоманды. Она занимает всю строку макрокоманды и имеет следу- ющий синтаксис:
:идентификатор_перехода
При расширении макрокоманды все макроидентификаторы перехода отбрасываются.
Директива GOTO сообщает Турбо Ассемблеру, что нужно перейти на заданную точку исходного кода, а именно - на "идентифика- тор_перехода". Это позволяет вам поместить GOTO в блоке условного ассемблирования. Например:
IF foo GOTO tag1 ENDIF DISPLAY "foo имеет значение false!" :tag ; возобновление макрокоманды. ; работает одинаково, независимо от того, ; равно foo false или true
Примечание: Будьте аккуратны и не создавайте при ис- пользовании директивы GOTO бесконечных циклов. Бесконечные циклы могут привести к тому, что Турбо Ассемблер исчерпает доступную память или даже прекратит работу.
Подробнее о директивах условного ассемблирования рассказыва- ется в Главе 15.

Директива GROUP

----------------------------------------------------------------
Директива GROUP может использоваться для присваивания сег- ментов группам. Группы позволяют вам для доступа ко всем сегмен- там группы задавать один сегмент.
В режиме Ideal директива GROUP имеет следующий синтаксис:
GROUP имя имя_сегмента [, имя_сегмента.]
В режиме MASM вы можете использовать следующий синтаксис:
имя GROUP имя_сегмента [, имя_сегмента.]
где "имя" представляет собой имя группы, а "имя_сегмента" - это имя сегмента, которое вы хотите присвоить группе.

Директива INSTR

Директива INSTR возвращает позицию одной строки внутри дру- гой. Она имеет следующий синтаксис:
имя INSTR [выражение_начала,]строка_1,строка_2
Турбо Ассемблер присваивает "имя" числовому значению, кото- рое соответствует первой позиции первого вхождения "строки_2" в "строку_1". Первый символ "строки_1" имеет позицию 1. Если "стро- ка_2" в "строке_1" не содержится, то Турбо Ассемблер возвращает значение 0. Если вы укажете "выражение_начала", то поиск начина- ется с символа, задаваемого этим выражением. Первый символ строки находится в позиции 1.

Директива компилятора $L и внешние подпрограммы

Два ключевых момента при использовании Турбо Ассемблера с Турбо Паскалем - это директива компилятора (Турбо Паскаля) {$L} и описание внешней (external) подпрограммы. Директива {$L MYFILE.OBJ} приводит к тому, что Турбо Паскаль будет искать файл объектный MYFILE.OBJ (файл в стандартном пригодном для компоновки формате MS-DOS) и компоновать его с вашей программой Турбо Паска- ля. Если у файла в директиве {$L} расширение не указывается, то подразумевается расширение .OBJ.
Каждая процедура или функция Турбо Ассемблера, которую вы хотите сделать доступной в программе Турбо Паскаля, должна объяв- ляться, как идентификатор PUBLIC, и ей должно соответствовать в программе описание external (внешняя). Синтаксис описания внешней процедуры или функции в Турбо Паскале аналогичен опережающему (forward) описанию:
procedure AsmProc(a : integer; b : real); external;
function AsmFunc(c : word; d : byte); external;
Эти описания должны соответствовать следующим описаниям в программе Турбо Ассемблера:
CODE SEGMENT BYTE PUBLIC AsmProc PROC NEAR PUBLIC AsmProc . . . AsmProc ENDP AsmFunc PROC FAR PUBLIC Bar . . . AsmFunc ENDP CODE ENDS
Описание внешней (external) процедуры Турбо Паскаля должно находиться на самом внешнем уровне программы или модуля, то есть оно не должно быть вложенным по отношению к другому описанию про- цедуры или функции. Попытка описать процедуру или функцию на лю- бом другом уровне приведет к ошибке этапа компиляции.
Турбо Паскаль не делает проверку, чтобы убедиться, что все процедуры, описанные с атрибутами NEAR или FAR, соответствуют ближним или дальним подпрограммам в программе Турбо Паскаля. Фак- тически, он даже не проверяет, являются ли метки AsmProc и AsmFunc именами процедур. Поэтому вы должны обеспечить, чтобы описания в Ассемблере и Паскале были правильными.

Директива LABEL

Директиву LABEL вы можете использовать для определения иден- тификатора заданного типа. Заметим, что синтаксис в режимах MASM и Ideal различен. В режиме Ideal задавайте:
LABEL имя сложный_тип
В режиме MASM используйте следующее:
имя LABEL сложный_тип
где "имя" - это идентификатор, который вы не определяли ранее в исходном файле. "Сложный_тип" описывает размер идентификатора и то, ссылается он на код или данные. См. Главу 5, в которой можно найти дальнейшую информацию о сложных типах.
Если вы не используете директиву PUBLIC, чтобы метка была доступна из других файлов, она доступна только в текущем исходном файле.
Вы можете использовать директиву LABEL для доступа к элемен- там различного размера, которые содержатся в структуре данных. Этот принцип иллюстрируется следующим примером:
WORDS LABEL WORD ; доступ к "BYTES" как к ; WORDS BYTES DB 64 DUP (0) mov WORDS[2],1 ; запись в WORDS 1

Директива MODEL

Директива MODEL позволяет вам задать для программы несколько стандартных моделей сегментации. Вы можете также использовать ее для задания языка для процедур программы.
Директива MODEL имеет следующий синтаксис:
MODEL [модификатор_модели] модель_памяти [имя_сегмента_кода] [,[модификатор_языка] язык] [, модификатор_модели]
В режиме MASM используется тот же синтаксис, но директива имеет вид .MODEL.
"Модель_памяти" и "модификатор_модели" определяют модель сегментации памяти, используемую в программе.
В применяемых в Турбо Ассемблере стандартных моделях можно использовать специальные сегменты для:
- кода;
- инициализированных данных;
- неинициализированных данных;
- инициализированных данных дальнего типа;
- неинициализированных данных дальнего типа;
- констант;
- стека.
Сегмент кода содержит обычно код модуля (но при необходимос- ти может также содержать данные). В целях совместимости с языками высокого уровня инициализированные данные и константы интерпрети- руются по-разному. Они содержат такие данные, как сообщения, ког- да важно начальное значение. Неинициализированные данные и стек содержат данные, начальные значения которых не существенны. Ини- циализированные данные дальнего типа (far) - это неинициализиро- ванные данные, которые не являются частью стандартного сегмента данных, и которые доступны только при изменении значения сегмент- ного регистра. Это же относится к неинициализированным данным дальнего типа, только вместо инициализированных данных здесь со- держатся неинициализированные данные.
Специальные модели памяти задают, как можно ссылаться на эти сегменты с помощью сегментных регистров, и как они объединяются в группы (если это имеет место). При написании программы вы должны хранить эти сегменты отдельно, независимо от размера программы. Затем для объединения сегментов в группы вы можете выбрать соот- ветствующую модель. Если вы храните эти сегменты раздельно, и ва- ша программа увеличивается в размере, вы можете использовать большую модель.

Единственным обязательным параметром директивы MODEL являет- ся модель памяти. Каждая стандартная модель памяти описывается в Таблице 7.1.

Поле "модификатор_модели" позволяет вам изменить отдельные аспекты модели. Вы можете задавать при необходимости несколько модификаторов модели. Доступные модификаторы модели приведены в Таблице 7.2.

Заметим, что в целях совместимости с MASM 5.2 вы можете за- давать спецификатор модели в двух местах. Если вы не используете спецификатор модели, Турбо Ассемблер подразумевает спецификатор NEARSTACK и USE32 (при выборе процессоров 80386 или 80486). Если не задано обратное, то основой считается DOS.

В больших моделях кода для переопределения используемого по умолчанию имени сегмента кода используется необязательное поле "имя_сегмента_кода". Обычно это имя модуля с присоединенным к нему именем _TEXT.

Стандартные модели памяти Таблица 7.1 ----------T---------T--------T--------------T-------------------¬ ¦Модель ¦ Код ¦Данные ¦Предполагаемые¦Описание ¦ ¦ ¦ ¦ ¦регистры ¦ ¦ +---------+---------+--------+--------------+-------------------+ ¦TINY ¦ ближний ¦ближний ¦cs=dgroup ¦Весь код и все дан-¦ ¦ ¦ ¦ ¦ds=ss=dgroup ¦ные комбинируются¦ ¦ ¦ ¦ ¦ ¦в одну группу с¦ ¦ ¦ ¦ ¦ ¦именем DGROUP. Эта¦ ¦ ¦ ¦ ¦ ¦модель используется¦ ¦ ¦ ¦ ¦ ¦для программ, ас-¦ ¦ ¦ ¦ ¦ ¦семблируемых в фор-¦ ¦ ¦ ¦ ¦ ¦мат .COM. Некоторые¦ ¦ ¦ ¦ ¦ ¦языки эту модель не¦ ¦ ¦ ¦ ¦ ¦поддерживают. ¦ ¦ ¦ ¦ ¦ ¦ ¦ +---------+---------+--------+--------------+-------------------+ ¦SMALL ¦ ближний ¦ближний ¦cs=_text ¦Код представляет¦ ¦ ¦ ¦ ¦ds=ss=dgroup ¦собой один сегмент.¦ ¦ ¦ ¦ ¦ ¦Все данные комбини-¦ ¦ ¦ ¦ ¦ ¦руются в группу с¦ ¦ ¦ ¦ ¦ ¦именем DGROUP. Это¦ ¦ ¦ ¦ ¦ ¦наиболее общая мо-¦ ¦ ¦ ¦ ¦ ¦дель, использующая-¦ ¦ ¦ ¦ ¦ ¦ся для автономных¦ ¦ ¦ ¦ ¦ ¦программ на Ассемб-¦ ¦ ¦ ¦ ¦ ¦лере. ¦ ¦ ¦ ¦ ¦ ¦ ¦ +---------+---------+--------+--------------+-------------------+ ¦MEDIUM ¦ дальний ¦ближний ¦cs= ¦Для кода использу-¦ ¦ ¦ ¦ ¦<модуль>_text ¦ется несколько сег-¦ ¦ ¦ ¦ ¦ds=ss=dgroup ¦ментов, по одному¦ ¦ ¦ ¦ ¦ ¦на модуль. Данные¦ ¦ ¦ ¦ ¦ ¦находится в группе¦ ¦ ¦ ¦ ¦ ¦с именем DGROUP. ¦ ¦ ¦ ¦ ¦ ¦ ¦ +---------+---------+--------+--------------+-------------------+ ¦COMPACT ¦ ближний ¦дальний ¦cs=_text ¦Код находится в од-¦ ¦ ¦ ¦ ¦ds=ss=dgroup ¦ном сегменте. Все¦ ¦ ¦ ¦ ¦ ¦ближние данные на-¦ ¦ ¦ ¦ ¦ ¦ходятся в группе с¦ ¦ ¦ ¦ ¦ ¦именем DGROUP. Для¦ ¦ ¦ ¦ ¦ ¦ссылки на данные¦ ¦ ¦ ¦ ¦ ¦используются даль-¦ ¦ ¦ ¦ ¦ ¦ние указатели. ¦ ¦ ¦ ¦ ¦ ¦ ¦ +---------+---------+--------+--------------+-------------------+ ¦LARGE ¦ дальний ¦дальний ¦cs= ¦Для кода использу-¦ ¦ ¦ ¦ ¦<модуль>_text ¦ется несколько сег-¦ ¦ ¦ ¦ ¦ds=ss=dgroup ¦ментов, по одному¦ ¦ ¦ ¦ ¦ ¦на модуль. Все¦ ¦ ¦ ¦ ¦ ¦ближние данные на-¦ ¦ ¦ ¦ ¦ ¦ходятся в группе с¦ ¦ ¦ ¦ ¦ ¦именем DGROUP. Для¦ ¦ ¦ ¦ ¦ ¦ссылки на данные¦ ¦ ¦ ¦ ¦ ¦используются даль-¦ ¦ ¦ ¦ ¦ ¦ние указатели. ¦ ¦ ¦ ¦ ¦ ¦ ¦ +---------+---------+--------+--------------+-------------------+ ¦HUGE ¦ дальний ¦дальний ¦cs= ¦То же, что модель¦ ¦ ¦ ¦ ¦<модуль>_text ¦LARGE (что касается¦ ¦ ¦ ¦ ¦ds=ss=dgroup ¦Турбо Ассемблера). ¦ ¦ ¦ ¦ ¦ ¦ ¦ +---------+---------+--------+--------------+-------------------+ ¦TCHUGE ¦ дальний ¦дальний ¦cs= ¦Это эквивалентно¦ ¦ ¦ ¦ ¦<модуль>_text ¦модели LARGE, но с¦ ¦ ¦ ¦ ¦ds=nothing ¦другими предположе-¦ ¦ ¦ ¦ ¦ss=nothing ¦ниями о сегментных¦ ¦ ¦ ¦ ¦ ¦регистрах. ¦ ¦ ¦ ¦ ¦ ¦ ¦ +---------+---------+--------+--------------+-------------------+ ¦TPASCAL ¦ ближний ¦дальний ¦cs=code, ds ¦Эта модель поддер- ¦ ¦ ¦ ¦ ¦=data, ss= ¦живается ранними¦ ¦ ¦ ¦ ¦nothing ¦версиями Турбо Пас-¦ ¦ ¦ ¦ ¦ ¦каля. В более позд-¦ ¦ ¦ ¦ ¦ ¦них версиях не тре-¦ ¦ ¦ ¦ ¦ ¦буется. ¦ ¦ ¦ ¦ ¦ ¦ ¦ +---------+---------+--------+--------------+-------------------+ ¦FLAT ¦ ближний ¦ближний ¦cs=_text ¦То же, что и модель¦ ¦ ¦ ¦ ¦ds=ss=flat ¦SMALL, но подходит¦ ¦ ¦ ¦ ¦ ¦для использования в¦ ¦ ¦ ¦ ¦ ¦OS/2. ¦ L---------+---------+--------+--------------+--------------------


Модификаторы модели Таблица 7.2 -----------------------------T----------------------------------¬ ¦ Модификатор модели ¦ Функция ¦ +----------------------------+----------------------------------+ ¦ NEARSTACK ¦ Указывает, что сегмент стека дол-¦ ¦ ¦ жен включаться в DROUP (если¦ ¦ ¦ группа DGROUP присутствует), а SS¦ ¦ ¦ должен указывать на DGROUP. ¦ ¦ ¦ ¦ ¦ FARSTACK ¦ Указывает, что сегмент стека не¦ ¦ ¦ должен включаться в DGROUP, а SS¦ ¦ ¦ должен указывать не nothing (не¦ ¦ ¦ определен). ¦ ¦ ¦ ¦ ¦ USE16 ¦ Задает, что все сегменты в¦ ¦ ¦ выбранной модели должны быть 16-¦ ¦ ¦ разрядными (при выборе процессора¦ ¦ ¦ 80386 или 80486). ¦ ¦ ¦ ¦ ¦ USE32 ¦ Задает, что все сегменты в¦ ¦ ¦ выбранной модели должны быть 32-¦ ¦ ¦ разрядными (при выборе процессора¦ ¦ ¦ 80386 или 80486). ¦ ¦ ¦ ¦ ¦ DOS, OS_DOS ¦ Задает, что прикладная программа¦ ¦ ¦ ориентируется на DOS. ¦ ¦ ¦ ¦ ¦ OS2, OS_OS2 ¦ Задает, что прикладная программа¦ ¦ ¦ ориентируется на DOS. ¦ L----------------------------+-----------------------------------

"Язык" и "модификатор_языка" вместе определяют соглашения, используемые при вызове процедуры, а также используемый по умол- чанию характер начала и завершения кода каждой процедуры. Они оп- ределяют также как будут объявляться общедоступные идентификато- ры (которые использует компоновщик). Турбо Ассемблер будет автоматически генерировать код входа и выхода для каждой процеду- ры, используя одно из следующих языковых соглашений: PASCAL, C, CPP (C++), SYSCALL, BASIC, FORTRAN, PROLOG и NOLANGUAGE (язык не задан). Если вы не задаете язык, то Турбо Ассемблер предполагает использование NOLANGUAGE.

Используйте "модификатор_языка" для задания кода начала и завершения процедур для WIndows или оверлейного загрузчика фирмы Borland. Можно задавать параметры NORMAL, WINDOWS, ODDNEAR и ODDFAR. Если вы не задаете параметр, то Турбо Ассемблер по умол- чанию подразумевает NORMAL.

Примечание: Более подробно об этом рассказывается в Главе 16.

Кроме того, вы можете переопределить используемый по умолча- нию язык и модификатор языка при определении процедуры. Подроб- ности можно узнать в Главе 10.

При объявлении общедоступного идентификатора вы можете также переопределить язык.

Директива NAME

Используйте директиву NAME для задания имени модуля объект- ного файла. Она имеет следующий синтаксис:
NAME имя_модуля
Примечание: Данная директива работает только в режиме Ideal.
Турбо Ассемблер обычно использует в качестве имени модуля имя исходного файла с дисководом, каталогом и расширением. Если вы хотите изменить это используемое по умолчанию имя, укажите ди- рективу NAME. Новым именем модуля будет имя "имя_модуля", напри- мер:
NAME loader

Директива ORG

Вы можете использовать директиву ORG, чтобы установить счетчик адреса в значение текущего сегмента. Директива ORG имеет следующий синтаксис:
ORG выражение
где "выражение" не может содержать никаких имен идентификаторов с опережающими ссылками. Оно может быть константой или смещением от идентификатора в текущем сегменте, либо смещением относительно текущего счетчика адреса.
Перед данными или кодом, которые уже введены в сегмент, вы можете восстановить значение счетчика адреса. Это можно использо- вать для возврата назад и заполнения записей таблицы, значения которых не были известны во время определения таблицы. Используй- те этот метод с осторожностью: вы можете непреднамеренно что-либо затереть.
Директиву ORG можно использовать для того, чтобы связать метку с конкретным адресом. Директива ORG может также использо- ваться для задания начального адреса файлов .COM. Приведем приме- ры ее использования:
PROG SEGMENT ORG 100h ; начальное смещение файла .COM start: ; ; остальная часть программы .COM PROG ENDS end start
Приведем другой пример:
PROG SEGMENT ASSUME cs:PROG, ds:PROG, ss:PROG, es:NOTHING ORG 20h EnvPtr label word ; определить метку для доступа ; к слову в PSP, которое ука- ; зывает на сегмент, содержа- ; щий блок операционной среды ; (он обычно освобождается для ; создания более компактной ; резидентной программы) ORG 80h CmdLength lebel byte ; определить метку для доступа ; к длине командной строки
ORG @1h CmdLine label byte ; определить метку для доступа ; к тексту командной строки
ORG 0100h start:
; освободить блок операционной среды
mov es, EnvPtr ; ES -> блок операционной среды mov ah, 49h ; функция 49h: освободить блок ; памяти int 21h ; вызвать MS-DOS jc error_ENV ; ошибка освобождения EnvBlock?
; анализ командной строки
mov al, CmdLength ; длина командной строки or al,al ; проверка на 0 jz no_params ; нет параметров mov cl,al ; поместить длину в cl mov ch,0 mov si,offset CmdLine ; адрес командной строки mov al,' ' ; символ для поиска repne scasb ; поиск первого пробела ; ; остальная часть файла .COM резидентной программы: PROG ENDS end start

Директива PUBLIC

В Турбо Паскале доступны только те метки Ассемблера, которые объявлены в модуле на языке Ассемблера, как общедоступные (PUBLIC). Метки представляют собой единственные объекты, которые могут передаваться из языка Ассемблера в Турбо Паскаль. Более того, каждой общедоступной метке должно соответствовать описание процедуры или функции в программе Турбо Паскаля, иначе компилятор выдаст сообщение об ошибке. Причем не требуется, чтобы общедос- тупная метка была частью описания PROC. Что касается Турбо Паска- ля, то для него описания:
AsmLabel PROC FAR PUBLIC Bar и
AsmLabel: PUBLIC Bar
эквивалентны.
Вы можете определять такие идентификаторы как PUBLIC только в сегменте CODE. Турбо Паскаль не разрешает определения идентифи- каторов PUBLIC в сегменте данных, поэтому создаваемые вами ас- семблерные модули для компоновки с программой на Турбо Паскале также не должны иметь в сегменте данных.

Директива SEGMENT

Директива SEGMENT открывает сегмент. Все последующие код или данные до директивы ENDS, которая закрывает сегмент, будут вклю- чены в этот сегмент.
В режиме Ideal директива SEGMENT имеет следующий синтаксис:
SEGMENT имя [атрибуты]
В режиме MASM директива SEGMENT имеет следующий синтаксис:
имя SEGMENT [атрибуты]
где "имя" - это имя сегмента. Назначать сегментам имена нужно в соответствии с их использованием. Примеры имен сегментов можно найти в Приложении A.
В одном сегменте вы можете несколько раз открыть и закрыть сегмент с одним и тем же именем. Турбо Ассемблер выполняет конка- тенацию всех частей сегмента. Атрибуты сегмента нужно задавать только при первом его открытии.
Атрибуты могут включать в себя все или некоторые значения атрибутов сегмента, которые определяют следующее:
- атрибут комбинирования сегмента;
- атрибут класса сегмента:
- атрибут выравнивания сегмента;
- атрибут размера сегмента;
- атрибут доступа к сегменту.
Примечание: Заметим, что Турбо Ассемблер обрабатывает значения атрибутов слева-направо.

Директива .SEQ

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

Директива SIZESTR

Директива SIZESTR возвращает длину текстовой макрокоманды (число символов в строке). Она имеет синтаксис:
имя SIZESTR строка
где "имя" устанавливается в числовое значение, равное длине ука- занной строки. Нулевая строка <> имеет длину 0.

Директива STARTUPCODE

Директива STARTUPCODE обеспечивает код инициализации, соот- ветствующий текущей модели и операционной системе. Она отмечает также начало программы. Эта директива имеет следующий синтаксис:
STARTUPCODE
или
.STARTUP ; (только для режима MASM)
Директива STARTUPCODE инициализирует регистры DS, SS и SP. Для моделей SMALL, MEDUIUM, COMPACT, LARGE, HUGE и TPASCAL Турбо Ассемблер устанавливает DS и SS в @data, а SP - в конец стека. Для моделей TINY и TCHUGE директива STARTUPCODE не изменяет сег- ментных регистров.

Директива SUBSTR

Директива SUBSTR определяет новую текстовую макрокоманду, которая является подстрокой строки. Она имеет следующий синтак- сис:
имя SUBSTR строка,выражение_позиции[,выражение_размера]
Новая текстовая макрокоманда с именем "имя" состоит из части строки , начинающейся с символа в позиции "выражение_позиции" и длины "выражение_длины". Если вы не указываете "выражение_разме- ра", новая текстовая макрокоманда содержит остаток строки, начи- ная с символа в "выражении_позиции". Турбо Ассемблер рассматрива- ет первый символ строки как позицию 1.

Директива TBLPTR

В концепции объектов используется таблица виртуальных мето- дов. Экземпляр этой таблицы существует для любого объекта, содер- жащего виртуальные методы. Структура данных для для любого объек- та, содержащего виртуальные методы, также должна содержать указа- тель на таблицу виртуальных методов этого объекта. Турбо Ассемблер автоматически обеспечивает в структуре данных объекта указатель таблицы виртуальных методов (если это требуется), и вам не нужно задавать его явно с помощью директивы TBLPTR.
Директиву TBLPTR следует использовать в определении структу- ры данных объекта. TBLPTR позволяет вам явно разместить указатель таблицы виртуальных методов там, где вы хотите. Она имеет следую- щий синтаксис:
TBLPTR
Размер указателя, который резервирует TBLPTR, определяется тем, задано в текущей модели USE16 или USE32, и тем, какие моди- фикаторы используются в определении объекта.

Директива условного ассемблирования IFxxx

Вы можете использовать директиву условного ассемблирования IFxxx для определения блока кода, который включается в объектный файл при удовлетворении определенных условий (таких как определе- ние идентификатора или установка какого-то значения). Приведем синтаксис оператора условного ассемблирования:
IFxxx тело_условия_true EMDIF
или IFxxx тело_условия_true ELSE тело_условия_false ENDIF
где IFxxx представляет следующие директивы:
IF IF1 IF2 IFDEF IFNDEF IFB IFNB IFIDN IFIDNI IFDIF IFDIFI
Каждая директива условного ассемблирования IFxxx задает конкретное условие, при вычислении которого получается истинное (true) или ложное (false) значение. Если условие имеет значение true, то выполняется ассемблирование и помещение в объектный файл блока ассемблируемого кода "тело_условия_true". Если при вычисле- нии условия получается значение false, то Турбо Ассемблер пропус- кает "тело_условия_true" и не включает его в объектный файл. При наличие директивы ELSE, если условие имеет значение false, то ас- семблируется и выводится в объектный файл блок "тело_усло- вия_false". Если условие имеет значение true, то этот блок игно- рируется. Условный блок завершается директивой ENDIF.
Кроме особых случаев директив IF1 и IF2 (которые мы обсудим позднее) два описанных блока кода являются взаимноисключающими: в объектный файл включается либо "тело_условия_true", либо "тело_ условия_false", но не оба блока одновременно. Кроме того, если вы используете форму IFxxx.ELSE.ENDIF, один из блоков будет обя- зательно включаться в объектный файл. Если используется форма IFxxx.ENDIF, то "тело_условия_true" может включаться или не включаться в объектный файл, в зависимости от условия.
При использовании вложенных директив IF и ELSE директива ELSE всегда соответствует ближайшей директиве IF.
В следующем примере test является идентификатором, который управляет включением кода (если идентификатор не определен, то генерируется соответствующий блок кода) Идентификатор color уста- навливается в ненулевое значение, если дисплей цветной, и равен 0, если дисплей монохромный.

От этих значений зависит фактическая генерация кода:

. . . IFDEF test ; true, если test определен ; первая часть блока кода ; если test определен IF color ; true, если color <> 0 ; блок кода color ; если color <> 0 ELSE ; ; код для монохромного ; дисплея если color = 0 ENDIF ; ; вторая часть блока кода ; если test определен ELSE ; ; другой код ; если test не определен ENDIF . . .

-------------T----------T-----------T--------------T------------¬ ¦ test ¦определен ¦ определен ¦ не определен ¦не определен¦ ¦ color ¦ 0 ¦ не 0 ¦ 0 ¦ не 0 ¦ +------------+----------+-----------+--------------+------------+ ¦ код ¦ блок кода¦ блок кода ¦ другой код ¦другой код ¦ ¦ ¦ 1 ¦ 1 ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦цветной/моно¦ блок кода¦ блок кода ¦ блок кода ¦ ¦ ¦ ¦ color ¦ 2 ¦ 2 ¦ ¦ L------------+----------+-----------+--------------+-------------

Если идентификатор test не определен,то не ассемблируется ни код для цветного дисплея (color), ни для монохромного, поскольку все это включено в блок условного ассемблирования, который ас- семблируется только при определенном идентификаторе test.

Директива VERSION

Использование директивы VERSION позволяет вам определить, для какой версии Турбо Ассемблера или MASM вы пишете конкретные модули. Это полезно использовать для совместимости (снизу вверх или наоборот) различных версий TASM и MASM. Директива VERSION также переводит вас в режим работы заданной версии.
Директиву VERSION вы можете указывать как в виде параметра командной строки, так и в исходном коде программы.
В исходном коде она имеет следующий синтаксис:
VERSION <идентификатор_версии>
Допускается задавать следующие идентификаторы версии:
M400 MASM 4.0 M500 MASM 5.0 M510 MASM 5.1 M520 MASM 5.2 (Quick ASM) T100 Турбо Ассемблер 1.0 T101 Турбо Ассемблер 1.01 T200 Турбо Ассемблер 2.0 T250 Турбо Ассемблер 2.5 T300 Турбо Ассемблер 3.0
При использовании данной директивы в командной строке ис- пользуется следующий синтаксис:
/U<номер_версии>
Например, если вы хотите ассемблировать программу, написан- ную для MASM 5.0, то можно не изменять исходный код и указать в командной строке параметр /uM510.
Здесь действуют следующие правила:
1. Директива VERSION по умолчанию всегда выбирает режим MASM, поскольку это начальный режим работы как для MASM, так и для Турбо Ассемблера.
2. Директива VERSION ограничивает высокоприоритетные ключе- вые слова теми словами, которые доступны для заданного компилятора и версии, В результате некоторые средства, которые были добавлены в последние версии, будут вам не- доступны.
3. Если в режиме Ideal вы выбираете версию < T300, то дирек- тива VERSION вам недоступна. В этом случае, чтобы исполь- зовать директиву VERSION, вы должны сначала переключиться в режим MASM.
В предыдущих версиях Турбо Ассемблера совместимость с MASM обеспечивалась с помощью таких директив, как MASM51, MOMASM51, QUIRKS, SMART и NOSMART. Вместо этих директив используется теперь директива VERSION. Полное описание ключевых слов, доступных в каждой предыдущей версии Турбо Ассемблера, можно найти в Приложе- нии B.

Директива WHILE

Для повторения тела макрокоманды до того момента, пока опре- деленное выражение не примет значения 0 (false), вы можете ис- пользовать макродирективу WHILE. Она имеет следующий синтаксис:
WHILE выражение тело_макрокоманды ENDM
Перед каждой итерацией тела макрокоманды Турбо Ассемблер вы- числяет "выражение". Будьте аккуратны и избегайте бесконечных циклов, которые привести к тому, что Турбо Ассемблер исчерпает доступную память или прекратит работу. Приведем пример использо- вания директивы WHILE:
WHILE 1 ;; ничего не делает ENDM

Директива ::

Директива :: позволяет вам определить с областью действия, выходящей за рамки процедуры, в которой она находится. Это отли- чается от директивы : тем, что метки, определенные с помощью : имеют область действия только в текущей процедуре. Заметим, что :: отличается от : только когда вы задаете язык в операторе .MODEL. Приведем пример (файл DBLCOLON.ASM):
VERSION M510 .MODEL SMALL,C .CODE
A PROC NOP ASINGLE:NOP ADOUBLE::NOP NOP A ENDP
B PROC NOP JMP ASINGLE ; приведет к ошибке JMP ADOUBLE RET B ENDP END
Назад | Содержание | Вперед

Директивы EVEN и EVENDATA

Вы можете использовать директиву EVEN для округления счетчи- ка адреса до следующего четного адреса. Директива EVEN позволяет вам выравнивать код для эффективного доступа к процессорам, ис- пользующим 16-разрядную шину данных. Производительность процессо- ров, использующих 8-разрядную шину данных, директивой EVEN не улучшается.
Директива EVENDATA выполняет выравнивание на четную границу, изменяя счетчика адреса без генерации данных, что полезно исполь- зовать для неинициализированных сегментов. Директивы EVEN и EVENDATA приводят к тому, что если выравнивание текущего сегмента выполнено недостаточно строго, Турбо Ассемблер выводит предупреж- дающее сообщение.
Если когда встречается директива EVEN счетчик адреса имеет нечетное значение, то Турбо Ассемблер помещает в сегмент один байт или инструкцию NOP, чтобы счетчик адреса стал четным. С по- мощью заполнения инструкциями NOP директиву EVEN можно в сегмен- тах кода, не вызывая на этапе работы программы выполнения невер- ных инструкций. Если счетчик адреса уже имеет четное значение, то данная директива не действует.
Аналогично, если счетчик адреса имеет нечетное значение, когда встречается директива EVENDATA, Турбо Ассемблер генерирует неинициализированный байт.
Приведем следующий пример использования директивы EVEN:
EVEN @@A: lodsb xor bl,al ; выравнивание для эффективного доступа loop @@a
Приведем пример использования директивы EVENDATA:
EVENDATA VAR1 DW 0 ; выравнивание для эффективного ; доступа 8086

Директивы генерации сообщений об ошибке ERRxx

Директива ERRxxx генерирует при удовлетворении определенных условий сообщения пользователя об ошибке. Она имеет следующий об- щий синтаксис:
ERRxxx [аргументы] [сообщение]
В этом случае директива ERRxxx представляет какую-либо из директив условной генерации сообщения об ошибке (такие как ERRIFB, .ERRB и т.д.).
"Аргументы" представляют аргументы, которые могут потребо- ваться в директиве для вычисления условия. Некоторые директивы требуют выражения, другие требуют символьного выражения, а неко- торые - одно или два текстовых выражений. Некоторые из директив вовсе не требуют аргументов.
Если указано "сообщение", то оно задает необязательное сооб- щение, которое выводится с ошибкой. Сообщение должно быть заклю- чено в кавычки (' или ").
Директивы генерации сообщений об ошибке генерируют пользова- тельское сообщение об ошибке, которое выводится на экран и включается в файл листинга (если он имеется) в месте расположения директивы в исходном коде. Если директива задает сообщение, оно выводится на той же строке непосредственно за ошибкой. Например, директива:
ERRIFNDEF foo "foo не определено!"
если идентификатор foo не определен при обнаружении ошибки, при- ведет к генерации ошибки:
User error: "foo не определено!"

Директивы эмуляции сопроцессора

Если вам нужны реальные инструкции с плавающей точкой, то нужно использовать сопроцессор 8087. Если у вас установлен пакет программной эмуляции инструкций с плавающей точкой, то для его использования можно указать директиву EMUL. (Директива EMUL рабо- тает аналогично параметру /e).
Примечание: Директивы EMUL и NOEMUL работают как в ре- жиме MASM, так и в режиме Ideal.
Например:
Finit ; реальные инструкции сопроцессора ; 8087 EMUL Fsave BUF ; эмулируемая инструкция
При использовании сопроцессора 8087 вы можете либо эмулиро- вать инструкции с плавающей точкой с помощью директивы EMUL, либо принудительно использовать инструкции с плавающей точкой с по- мощью директивы NOEMUL. Заметим, что вы можете использовать инс- трукции EMUL и NOEMUL, когда хотите генерировать инструкции с плавающей точкой для одной части файла и эмулируемые инструкции для другой.
Приведем пример использования инструкции NOEMUL:
NOEMUL ; ассемблировать реальные инструкции ; с плавающей точкой finit EMUL ; вернуться к эмуляции
Назад | Содержание | Вперед

Директивы листинга включаемых файлов

В том случае, когда вы хотите вывести в файл листинга вклю- чаемые файлы, вы можете разрешить или запретить эту возможность с помощью директив %INCL и %NOINCL. По умолчанию включаемые файлы обычно включаются в файл листинга. Директива %NOINCL приостанав- ливает вывод в файл листинга всех последующих включаемых файлов, пока он снова не будет разрешен директивой %INCL. Это полезно ис- пользовать при наличии больших включаемых файлов, которые содер- жат, например, много определений EQU, которые никогда не изменя- ются.
Приведем пример:
%INCL INCLUSE DEFS.INC ; содержимое выводится в листинг %NOINCL INCLUSE DEF1.INC ; содержимое не выводится в листинг

Директивы LOCALS и NOLOCALS

Турбо Ассемблер использует двух символьный код, присоединяе- мый к идентификаторам в виде префикса и определяющий, что иденти- фикатор в процедуре имеет локальную область действия. Это префикс локального идентификатора обозначается как @@. Для разрешения идентификаторов, имеющих локальную область действия, можно ис- пользовать директиву LOCALS. Ее синтаксис имеет следующий вид:
LOCALS [префикс_идентификатора]
Необязательное поле "префикс_идентификатора" содержит иден- тификатор (размером в два символа), который Турбо Ассемблер будет использовать в качестве префикса локального идентификатора. Нап- ример:
LOCALS ; по умолчанию подразумевается использование ; в качестве префикса @@ foo proc @@a: jmp @@a ; этот идентификатор @@a относится к ; процедуре FOO foo endp
bar proc @@a: jmp @@a ; этот идентификатор @@a относится к про- ; цедуре BAR bar endp
Если вы хотите запретить идентификаторы с локальной областью действия, можно использовать директиву NOLOCALS. Она имеет следу- ющий синтаксис:
NOLOCALS
Заметим, что вы можете использовать идентификаторы с об- ластью действия в границах блока вне процедур. В этом случае об- ласть действия идентификатора определяется метками, заданными с помощью двоеточия, которые на являются идентификаторами с об- ластью действия в границах блока. Например:
foo: ; начало области действия @@a: ; относится к области действия, начинающейся ; FOO: @@b = 1 ; относится к области действия, начинающейся ; FOO: BAR: ; начало области действия @@a = 2 ; относится к области действия, начинающейся ; BAR:

Директивы макрокоманд работы с текстовыми строками

Турбо Ассемблер обеспечивает директивы, которые могут рабо- тать с текстовыми макрокоманд. Эти директивы доступны в режиме Ideal и для версий M510, M520 и T300 (как задается директивой VERSION).
Строковые аргументы для любой из этих директив могут быть следующими:
- текстовой строкой, заключенной в скобки, например, ;
- именем ранее определенной текстовой макрокоманды;
- выражением с предшествующим символом %, значение которого эквивалентно числовому представлению строки для текущего основания.

Процессоры семейства iAPx86 предусматривают использование

Процессоры семейства iAPx86 предусматривают использование множества директив. Заметим, что директивы, начинающиеся с точки, доступны только в режиме MASM.

Примечание: Список инструкций, допустимых для конкрет- ных процессоров, можно найти в Главе 1.

Директивы выбора процессоров iAPx86 Таблица 6.1 -----------------T----------------------------------------------¬ ¦ Директива ¦ Значение ¦ +----------------+----------------------------------------------+ ¦ P8086 ¦ Разрешает ассемблирование только инструкций ¦ ¦ ¦ процессора 8086. ¦ ¦ ¦ ¦ ¦ .8086 ¦ Разрешает ассемблирование инструкций проце- ¦ ¦ ¦ ссора 8086 и запрещает все инструкции, дос- ¦ ¦ ¦ тупные только на процессорах 80186, 80286 и ¦ ¦ ¦ 386. Разрешает также ассемблирование инст- ¦ ¦ ¦ рукций сопроцессора 8087, так же как инст- ¦ ¦ ¦ рукции .8087 или 8087. ¦ ¦ ¦ ¦ ¦ P186 ¦ Разрешает ассемблирования инструкций про- ¦ ¦ ¦ цессора 80186. ¦ ¦ ¦ ¦ ¦ .186 ¦ Разрешает ассемблирования инструкций про- ¦ ¦ ¦ цессора 80186. ¦ ¦ ¦ ¦ ¦ P286 ¦ Разрешает ассемблирование всех инструкций ¦ ¦ ¦ процессора 80286. ¦ ¦ ¦ ¦ ¦ P286N ¦ Разрешает ассемблирование непривилегирован- ¦ ¦ ¦ ных (реальный режим) инструкций процессора ¦ ¦ ¦ 80286 и инструкций арифметического сопро- ¦ ¦ ¦ цессора 80287. ¦ ¦ ¦ ¦ ¦ P286P ¦ Разрешает ассемблирование всех (включая ¦ ¦ ¦ защищенный режим) инструкций процессора ¦ ¦ ¦ 80286 и инструкций арифметического сопро- ¦ ¦ ¦ цессора 80287. ¦ ¦ ¦ ¦ ¦ .286 ¦ Разрешает ассемблирование непривилегирован- ¦ ¦ ¦ ных инструкций процессора 80286 (реальный ¦ ¦ ¦ режим) и инструкций арифметического сопро- ¦ ¦ ¦ цессора 80287, так же как директива P287. ¦ ¦ ¦ ¦ ¦ .286C ¦ Разрешает ассемблирование непривилегирован- ¦ ¦ ¦ ных инструкций процессора 80286 (реальный ¦ ¦ ¦ режим) и инструкций арифметического сопро- ¦ ¦ ¦ цессора 80287. ¦ ¦ ¦ ¦ ¦ .286P ¦ Разрешает ассемблирование всех инструкций ¦ ¦ ¦ процессора 80286 (включая защищенный режим) ¦ ¦ ¦ и инструкций арифметического сопроцессора ¦ ¦ ¦ 80287, как директивы .287 или .P287. ¦ ¦ ¦ ¦ ¦ P386 ¦ Разрешает ассемблирование всех инструкций ¦ ¦ ¦ процессора 386. ¦ ¦ ¦ ¦ ¦ P386N ¦ Разрешает ассемблирование непривилегирован- ¦ ¦ ¦ ных (реальный режим) инструкций процессора ¦ ¦ ¦ 386. ¦ ¦ ¦ ¦ ¦ P386P ¦ Разрешает ассемблирование всех (включая за- ¦ ¦ ¦ щищенный режим) инструкций процессора 386. ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ .386 ¦ Разрешает ассемблирование непривилегирован- ¦ ¦ ¦ ных инструкций процессора 80386 (реальный ¦ ¦ ¦ режим), включая все дополнительные инструк- ¦ ¦ ¦ ции, и инструкций арифметического сопроцес- ¦ ¦ ¦ сора 80387е, как директивы .387 и P387. ¦ ¦ ¦ ¦ ¦ .386C ¦ Разрешает ассемблирование инструкций проце- ¦ ¦ ¦ ссора 80386. ¦ ¦ ¦ ¦ ¦ .386P ¦ Разрешает ассемблирование всех инструкций ¦ ¦ ¦ процессора 80386 (включая защищенный режим) ¦ ¦ ¦ и инструкций арифметического сопроцессора ¦ ¦ ¦ 80387, как директивы .387 и P387. ¦ ¦ ¦ ¦ ¦ P486 ¦ Разрешает ассемблирование всех инструкций ¦ ¦ ¦ процессора i486 (включая защищенный режим). ¦ ¦ ¦ ¦ ¦ P486N ¦ Разрешает ассемблирование непривилегирован- ¦ ¦ ¦ ных инструкций процессора i486 (реальный ¦ ¦ ¦ режим). ¦ ¦ ¦ ¦ ¦ .486 ¦ Pазрешает ассемблирование дополнительных ¦ ¦ ¦ инструкций процессора, поддерживаемых ¦ ¦ ¦ процессором 80486 в непривилегированном ре- ¦ ¦ ¦ жиме. Разрешает также инструкции сопроцес- ¦ ¦ ¦ сора 3867, как директивы .387 и P387. ¦ ¦ ¦ ¦ ¦ .486C ¦ Разрешает ассемблирование всех инструкций ¦ ¦ ¦ процессора i486. ¦ ¦ ¦ ¦ ¦ .486P ¦ Разрешает ассемблирование всех инструкций ¦ ¦ ¦ процессора 80486 (включая защищенный режим) ¦ ¦ ¦ и инструкций арифметического сопроцессора ¦ ¦ ¦ 80487, как директивы .487 и P487. ¦ L----------------+-----------------------------------------------

Директивы счетчика адреса $

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

Имеющиеся директивы задания сопроцессора перечислены

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

Директивы сопроцессора 8087 Таблица 6.2 -----------------T----------------------------------------------¬ ¦ Директива ¦ Значение ¦ +----------------+----------------------------------------------+ ¦ .287 ¦ Разрешает ассемблирование всех инструкций ¦ ¦ ¦ арифметического сопроцессора 80287. Исполь- ¦ ¦ ¦ зуйте данную директиву, если вы знаете, что ¦ ¦ ¦ вам не потребуется запускать программы на ¦ ¦ ¦ машине с сопроцессором 8087. Данная дирек- ¦ ¦ ¦ тива приводит к оптимизации инструкций. При ¦ ¦ ¦ этом они становятся несовместимыми с сопро- ¦ ¦ ¦ цессором 8087. Поэтому не используйте дан- ¦ ¦ ¦ ную директиву, если ваши программы должны ¦ ¦ ¦ работать на сопроцессоре 8087. ¦ ¦ ¦ ¦ ¦ .387 ¦ Разрешает ассемблирование всех инструкций ¦ ¦ ¦ арифметического сопроцессора 80387. Исполь- ¦ ¦ ¦ зуйте данную директиву, если вы знаете, что ¦ ¦ ¦ вам не потребуется запускать программы на ¦ ¦ ¦ машине с сопроцессором 8087. Данная дирек- ¦ ¦ ¦ тива приводит к оптимизации инструкций. При ¦ ¦ ¦ этом они становятся несовместимыми с сопро- ¦ ¦ ¦ цессором 8087. Поэтому не используйте дан- ¦ ¦ ¦ ную директиву, если ваши программы должны ¦ ¦ ¦ работать на сопроцессоре 8087. ¦ ¦ ¦ ¦ ¦ .8087 ¦ Разрешает ассемблирование инструкций ариф- ¦ ¦ ¦ метического сопроцессора и запрещает все ¦ ¦ ¦ инструкции, доступные для сопроцессоров ¦ ¦ ¦ 80287 и 80387. Этот режим инструкций сопро- ¦ ¦ ¦ цессора используется Турбо Ассемблером по ¦ ¦ ¦ умолчанию. ¦ ¦ ¦ ¦ ¦ P287 ¦ Разрешает ассемблирование инструкций ариф- ¦ ¦ ¦ метического сопроцессора 80287. ¦ ¦ ¦ ¦ ¦ P387 ¦ Разрешает ассемблирование инструкций ариф- ¦ ¦ ¦ метического сопроцессора 80287. ¦ ¦ ¦ ¦ ¦ P8087 ¦ Разрешает ассемблирование только инструк- ¦ ¦ ¦ ций арифметического сопроцессора 8087. В ¦ ¦ ¦ Турбо Ассемблере этот режим назначается по ¦ ¦ ¦ умолчанию. ¦ L----------------+-----------------------------------------------

Директивы условного ассемблирования ELSEIFxxx

Вы можете использовать директивы условного ассемблирования ELSEIFxxx как сокращенную форму, когда требуется использовать несколько директив IF. Директива ELSEIFxxx представляет собой эк- вивалент директивы ELSE, за которой следует вложенная директива IFxxx, но дает более компактный код. Например:
. . . IF mode EQ 0 ; кода для mode = 0 ELSEIF mode LT 5 ; код для coe = 14 ELSE ; код для mode = 5. ENDIF . . .
сравните его со следующим:
. . . IF mode EQ 0 ; кода для mode = 0 ELSE IF mode LT 5 ; код для coe = 14 ELSE ; код для mode = 5. ENDIF ENDIF . . .
Вне оператора IFxxx директиву ELSEIFxxx использовать нельзя.

Директивы вывода блоков условного ассемблирования

Когда в исходном коде у вас содержатся блоки условного ассемблирования, может оказаться желательным включение в листинг всей информации. Вывод блоков условного ассемблирования может по- мочь в тех случаях, когда вы хотите точно видеть поведение исход- ного кода. В Турбо Ассемблере предусмотрены следующие директивы вывода блоков условного ассемблирования:
- .LFCOND ; только для режима MASM
- .SFCOND ; только для режима MASM
- ,TFCOND ; только для режима MASM
- %CONDS
- %NOCONDS
Обычно Турбо Ассемблер не включает в листинг блоки условного ассемблирования.
Директива %CONDS выводит в файл листинга все операторы ус- ловного блока. При этом в файл листинга выводятся также все блоки с условием false. Директива .LFCOND работает аналогично директиве .LFCOND. Директива %NOCONDS предотвращает вывода в файл листинга блоков условного ассемблирования с условием false. Директива .SFCONDS работает аналогично директиве %NOCOND. Если вы хотите переключить режим вывода блоков условного ассемблирования, ис- пользуйте директиву .TFCOND.
Первая директива .TFCOND, которую обнаруживает Турбо Ассемб- лер, разрешает вывод в листинг всех блоков условного ассемблиро- вания. Если вы используете параметр командной строки /X, блоки условного ассемблирования будут сначала выводиться в листинг, а первая директива .TFCOND запретит их вывод. При каждом обнаруже- нии в исходном файле директивы .TFCOND состояние вывода блоков условного ассемблирования изменяется на обратное.
Для указания любой из этих директив поместите директиву на отдельной строке исходного кода. Она будут влиять на непосредс- твенно следующие за данной директивой блоки условного ассемблиро- вания.

Директивы вывода листинга перекрестных ссылок

В таблице идентификаторов листинга ссылок обычно выводится большой объем информации о метках, группах и сегментах, но там не сообщается, где определены сегменты, группы и метки и где они ис- пользуются. Информация, выводимая в таблице перекрестных ссылок, позволяет вам легко находить метки и следить за ходом выполнения программы при ее отладке.
Существует несколько способов разрешения включения информа- ции о перекрестных ссылках в файл листинга. Для получения инфор- мации о перекрестных ссылках для всего файла вы можете использо- вать параметр командной строки /c (подробности можно найти в Главе 2), либо включить в исходный код директивы, позволяющие вам разрешать и запрещать вывод перекрестных ссылок для отдельных частей исходного кода. Это следующие директивы:
- .CREF ; только для режима MASM - .XCREF ; только для режима MASM - %CREF - %NOCREF - %CREFALL - %CREFREF - %CREFUREF
Директивы %CREF и .CREF позволяют вам собрать информацию о перекрестных ссылках для всех идентификаторах в исходном файле, начиная с данной точки. Директивы %CREF и .CREF отменяют действие директив %NOCREF или .XCREF, которые запрещают сбор информации о перекрестных ссылках.
Директивы %CREF и .CREF имеют следующий синтаксис:
%CREF или .CREF
Директивы %NOCREF и .XCREF имеют синтаксис:
%NOCREF [идентификатор, .] или .XCREF [идентификатор, .]
Если вы используете директивы %NOCREF и .XCREF без указания идентификатора, то вывод перекрестных ссылок запрещается пол- ностью. Если вы указываете одно или более имен идентификаторов, то вывод перекрестных ссылок запрещается только для данных иден- тификаторов.
Директива %CREFALL выводит в листинг перекрестные ссылки для всех идентификаторов. Директива %CREFALL изменяет действие преды- дущей директивы %CREFREF (запрещающей вывод в таблицу перекрест- ных ссылок тех идентификаторов, на которые ссылки отсутствуют) на обратное. После указания директивы %CREFALL все встречающиеся да- лее в исходном файле идентификаторы будут включаться в листинг перекрестных ссылок. Турбо Ассемблер использует это режим по умолчанию а начале ассемблирования исходного файла.
Директивы %CREFALL, %CREFREF и %CREFUREF имеют следующий синтаксис:
%CREFALL
%CREFREF
%CREFUREF

Директивы вывода в файл листинга макрокоманд

Обычно макрорасширения включаются в файлы листинга. Наличие такой информации в файле листинга может оказаться очень полезной, когда вы хотите видеть, что делается в исходном коде. В Турбо Ас- семблере предусмотрено несколько директив, которые позволяют включать и выключать данное средство. Это следующие директивы:
- .LALL ; только для режима MASM - .SALL ; только для режима MASM - .XALL ; только для режима MASM - %MACS - %NOMACS
Директива %MACS разрешает листинг всех макрорасширений. Тоже самое делает директива .LALL, но она работает только в режиме MASM. Вы можете использовать эти директивы для включения макро- расширений в файл листинга.
Директива %MACS имеет следующий синтаксис:
%MACS
Директиву .LALL вы можете задать следующим образом:
.LALL
Если вы хотите подавить вывод всех операторов макрорасшире- ния, используйте директивы %NOMACS или .SALL. Заметим, что эти директивы можно использовать для выключения вывода макрорасшире- ния в файл листинга.
Директива %NOMACS имеет следующий синтаксис:
%NOMACS
Директива .SALL задается следующим образом:
.SALL
Доступная в режиме MASM директива .XALL позволяет вам выво- дить в листинг только те макрорасширения, которые генерируют код или данные. Она имеет следующий синтаксис:
.XALL

Для чего используется режим Ideal?

Есть множество причин, по которым вам следует использовать режим Ideal Турбо Ассемблера. Если вы изучаете язык Ассемблера, то сможете легко строить для получения желаемого эффекта выраже- ния и операторы режима Ideal. Вам не придется ходить вокруг да около и пробовать различные варианты, пока вы не используете инс- трукцию, которая делает именно то, что вы хотите. Если у вас име- ется опыт программирования на Ассемблере, то вы можете использо- вать средства режима Ideal Турбо Ассемблера для разработки сложных программ, использующих расширения языка, такие, например, как вложенные структуры и объединения.
В результате более четкого синтаксиса режим Ideal Турбо Ас- семблера позволяет ассемблировать файлы на 30% быстрее, чем в ре- жиме MASM. Чем больше объем ваших программ и программных комплек- сов, тем больше времени вы сэкономите при ассемблировании, перейдя в режим Ideal.
Строгие правила проверки типов, налагаемые режимом Ideal, позволяют Турбо Ассемблеру выявлять ошибки, которые в противном случае вы обнаружили бы только при отладке вашей программы или ее выполнении. Это аналогично тому, каким образом компиляторы языков высокого уровня оказывают вам помощь, отмечая сомнительные конс- трукции и несоответствие размеров данных.
Хотя в режиме Ideal Турбо Ассемблера в некоторых выражениях используется другой синтаксис, вы, тем не менее, можете писать программы, которые будут одинаково хорошо ассемблироваться как в режиме MASM, так и в режиме Ideal. Вы можете также в одном и том же исходном файле переключаться из режима MASM в режим Ideal и обратно так часто, насколько это необходимо. Это особенно полезно при экспериментировании со средствами режима Ideal, или при пре- образовании имеющихся программ, написанных в синтаксисе MASM. Вы можете переключиться в режим Ideal в новых частях программы, до- бавляемых к имеющемуся исходному коду. При этом в остальных час- тях программы сохранится полная совместимость с MASM.

Для чего в Турбо Ассемблере используются объекты?

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

Дополнительная быстрая инструкции непосредственного умножения

Для эффективной индексации массивов Турбо Ассемблер обеспе- чивает специальную операцию непосредственного умножения. Инструк- ция FASTIMUL решает типичную проблему, возникающую при создании массива структур. Для процессора 8086 инструкция непосредственно- го умножения недоступна. Даже на более развитых процессорах умно- жение с использованием сдвигов и сложений выполняется в некоторых обстоятельствах существенно быстрее, чем стандартная непосредс- твенная инструкция IMUL. На основе текущего назначения процессора инструкция Турбо Ассемблера FASTIMUL выбирает между наиболее эф- фективной доступной последовательностью сдвигов и сложений и не- посредственной операцией IMUL текущего процессора (если она име- ется). Инструкция FASTIMUL имеет следующий синтаксис::
FASTIMUL регистр_приемник, регистр/память_источник, значение
Данная инструкция очень напоминает тернарную операцию IMUL, доступную на процессорах 80186, 80286 и 80386. Регистр-приемник - это регистр размером в слово (или двойное слово при работе на процессора 80386). "Регистр/память_источник" - это регистр или адрес в памяти, который должен соответствовать размеру приемника. "Значение" - это фиксированная константа со знаком (множитель).
Инструкция FASTIMUL использует для выполнения своей функции комбинацию инструкций IMUL, MOV, NEG, SHL, ADD и SUB. При этом содержимое регистра-источника (или адреса памяти) теряется. Флаги процессора не изменяются.

В инструкциях цикла процессора 80386

В инструкциях цикла процессора 80386 в качестве регист- ра-счетчика может использоваться регистр CX или ECX. Стандартные мнемоники инструкций LOOP, LOOPE, LOOPZ, LOOPNE и LOOPNZ фирмы Intel выбирают регистр-счетчик на основе того, является ли теку- щий сегмент кода 32-битовым сегментом (тогда используется ECX) или 16-битовым (используется регистр CX).

Турбо Ассемблер имеет специальные инструкции, которые увели- чивают гибкость средства LOOP. Инструкции LOOP, LOOPE, LOOPWZ, LOOPWNE и LOOPWNZ используют в качестве счетчика регистр CX, не- зависимо от текущего сегмента. Аналогично, инструкции LOOPD, LOOPDE, LOOPDZ, LOOPDNE и LOOPDNZ используют в качестве счетчика регистр ECX.

Дополнительные инструкции для объектного программирования

При создании экземпляра объекта требуется инициализировать указатель таблицы виртуальных методом объекта (если он имеется), чтобы он указывал на корректную таблицу виртуальных методов. Ин- струкция TBLINIT позволяет вам сделать это автоматически. Эта ин- струкция имеет следующий синтаксис:
TBLINIT указатель_экземпляра_объекта
Поле "указатель_экземпляра__объекта" представляет собой ад- рес объекта, указатель таблицы виртуальных методов которого нужно инициализировать. Инструкция TBLINIT предполагает, что экземпляр объекта должен иметь тип текущего объекта (другими словами, не- посредственно предшествующее определение объекта определяет объ- ектный тип, который инициализирует TBLINIT). Например:
TBLINIT DS:ST
инициализирует указатель таблицы виртуальных методов объекта DS: SI (если она имеется).
Назад | Содержание | Вперед

Дополнительные инструкции ENTER и LEAVE

Инструкции ENTER и LEAVE используются для удаления из стека кадра процедуры. В зависимости от того, является текущий сегмент кода 16-битовым или 32-битовым, стандартные инструкции ENTER и LEAVE будут модифицировать либо регистры BP и SP, либо EBP и ESP. Если сегмент кода - это 32-битовый сегмент, а сегмент стека - 16- битовый, то данные инструкции могут оказаться неподходящими.
В Турбо Ассемблере предусмотрены 4 дополнительные инструк- ции, которые всегда выбирают конкретный размер стека, независимо от размера сегмента кода. Инструкции ENTERW и LEAVEW всегда выби- рают в качестве регистров кадра стека BP и SP, a ENTERD и LEAVED - регистры EBP и ESP.

Дополнительные инструкции работы с битами полей

Турбо Ассемблер может генерировать специальные последова- тельности инструкций для задания значений и извлечения значений из битовых полей, заданных с помощью оператора RECORD. Это позво- ляет вам писать код, независимый от фактического расположения по- ля в записи. При использовании их в сочетании с оператором ENUM, записи на языке ассемблера могут получить очень высокий уровень модульности. Список данных инструкций приведен в следующей табли- це:
Инструкции для установки и извлечения значения Таблица 13.5 ----------------T-----------------------------------------------¬ ¦ Инструкция ¦ Функция ¦ +---------------+-----------------------------------------------+ ¦ SETFIELD ¦ Устанавливает значение поля записи. ¦ ¦ ¦ ¦ ¦ GETFIELD ¦ Извлекает значение из поля записи. ¦ L---------------+------------------------------------------------
Инструкция SETFIELD
Инструкция SETFIELD генерирует код, устанавливающий значение поля записи. Она имеет синтаксис:
SETFIELD имя_поля регистр/память_приемник, регистр_источник
где "имя_поля" - имя поля записи, "регистр/память_приемник" для SETFIELD представляет собой регистр или адрес в памяти типа BYTE или WORD (DWORD для процессора 80386). "Регистр_источник" должен быть регистром того же или меньшего размера. Если источник мень- ше, чем приемник, то регистр-источник должен быть младшей частью другого регистра, имеющего тот же размер, что и приемник. Этот полный регистр называется рабочим регистром. Используйте данный регистр для сдвига значения в регистре-источнике, благодаря чему оно выравнивается по приемнику. Например:
FOO RECORD R0:1,R1:4,R2:3,R3:1 . . . SETFIELD F1 AX,BL ; рабочим регистром является BX SETFIELD F1 AX,BH ; недопустимо!
SETFIELD сдвигает регистр-источник, чтобы эффективно выров- нять его на поле приемника, выполняет операцию OR и помещает ре- зультат в регистр-приемник.
Примечание: Операция SETFIELD уничтожает все содержи- мое рабочего регистра.
Чтобы выполнить свою функцию, инструкция SETFIELD генерирует эффективную и расширенную последовательность инструкций XOR, XCHG, ROL, ROR, OR и MOVZX.
Операция SETFIELD не пытается очистить целевое поле перед выполнением над его значением операции OR. Если это необходимо, вы можете очистить поле явным образом, используя инструкцию MASKFLAG.

Дополнительные инструкции работы с флагами

Часто, чтобы улучшить эффективность и уменьшить размер кода, можно упростить инструкции работы с флагами. Например, если единственным желаемым результатом является установка конкретного бита в AX, а флаги процессора, на которые влияет данная инструк- ция, значение не имеют, инструкцию:
OR ax,1000h
можно упростить до инструкции:
OR, ah,10h
Турбо Ассемблер обеспечивает 4 дополнительных инструкции, которые обеспечивают эти функциональные возможности. Они показаны в следующей таблице:
Эффективные инструкции работы с флагами Таблица 13.4 ------------------T----------------T-----------------------------¬ ¦ Инструкция ¦ Функция ¦Соответствует инструкции ¦ +-----------------+----------------+-----------------------------+ ¦ SETFLAG ¦ Установка бит ¦ OR ¦ ¦ ¦ (бита) флага. ¦ ¦ ¦ ¦ ¦ ¦ ¦ MASKFLAG ¦ Размаскирование¦ AND ¦ ¦ ¦ бит (бита) ¦ ¦ ¦ ¦ флага. ¦ ¦ ¦ ¦ ¦ ¦ ¦ TESTFLAG ¦ Проверка бит ¦ TEST ¦ ¦ ¦ (бита) флага. ¦ ¦ ¦ ¦ ¦ ¦ ¦ FLIPFLAG ¦ Дополнение бит ¦ XOR ¦ ¦ ¦ (бита) флага. ¦ ¦ L-----------------+----------------+------------------------------
Эти инструкции можно использовать для улучшения модульности записей, например:
FOO RECORD R0:1,R1:4,R2:3,R3:1 . . . TESTFLAG AX,R0
В данном примере TESTFLAG будет генерировать наиболее эффек- тивные инструкции, независимо от того, где в записи находится R0.

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

Стандартная инструкция RET генерирует код, который соответс- твующим образом завершает текущую процедуру. Это включает в себя генерацию кода завершения процедуры, которая использует интер- фейсные соглашения языка высокого уровня. Даже если для процедуры используются соглашения NOLANGUAGE, инструкция RET в зависимости от описания процедуры (описана она как NEAR или FAR) будет гене- рировать различный код. Для процедуры NEAR Турбо Ассемблер гене- рирует дальнюю инструкцию возврата. Для процедуры FAR Турбо Ас- семблер генерирует ближнюю инструкцию возврата. (Вне процедуры всегда генерируется ближний возврат.)
Турбо Ассемблер включает в себя дополнительные инструкции, которые позволяют задавать генерацию нужных инструкций возврата (без кода завершения). Они перечислены в следующей таблице:
Инструкции возврата Таблица 13.2 ------------------T---------------------------------------------¬ ¦ Инструкция ¦ Функция ¦ +-----------------+---------------------------------------------+ ¦ RETN ¦ Всегда генерирует ближний возврат. ¦ ¦ ¦ ¦ ¦ RETNF ¦ Всегда генерирует дальний возврат. ¦ ¦ ¦ ¦ ¦ RETCODE ¦ Генерирует возврат, тип которого соответ- ¦ ¦ ¦ ствует текущей выбранной модели. Для модели ¦ ¦ ¦ TINY, SMALL, COMPACT и TPASCAL генерируется ¦ ¦ ¦ ближний возврат. Для модели MEDIUM, LARGE, ¦ ¦ ¦ HUGE и TCHUGE - дальний возврат. ¦ L-----------------+----------------------------------------------

Доступ к данным в сегменте, принадлежащем группе

В режиме Ideal любой элемент данных в сегменте, являющемся частью группы, рассматривается строго как элемент группы, а не сегмента. В Турбо Ассемблере для распознавания элемента данных, как элемента сегмента, нужно использовать явное переопределение сегмента.
В режиме MASM это интерпретируется по-другому. Иногда иден- тификаторы считается частью сегмента, а не частью группы. В част- ности, в режиме MASM идентификатор интерпретируется, как часть сегмента, когда он используется, как указатель на выделенные дан- ные. Это может вызвать путаницу, так как когда вы непосредственно обращаетесь к данным без операции OFFSET, MASM некорректно гене- рирует ссылку на сегмент вместо ссылки на группу.
Пример поможет нам пояснить, как легко можно нажить неприят- ности из-за специфики адресации в MASM. Рассмотрим следующую не- полную программу MASM, в которой описываются три сегмента данных:
dseg1 SEGMENT para public 'data'
v1 db 0 dseg1 ENDS
dseg2 SEGMENT para public 'data' v2 db 0 dseg2 ENDS
dseg3 SEGMENT para public 'data' v3 db 0 dseg3 ENDS
DGROUP GROUP dseg1,dseg2,dseg3 cseg SEGMENT para public 'code'
ASSUME cs:cseg,ds:DGROUP
start: mov ax,OFFSET v1 mov bx,OFFSET v2 mov cx,OFFSET v3 cseg ENDS END start
Три сегмента dseg1, dseg2 и dseg3 группируются под одним именем DGROUP. В результате все переменные отдельных сегментов хранятся в памяти вместе. В исходном тексте программы в каждом из отдельных сегментов описывается байтовая переменная (метки v1, v2 и v3).
В коде данной программы MASM в регистры AX, BX и CX загружа- ются смещения адресов этих переменных. В соответствии с предшест- вующей директивой ASSUME и из-за того, что сегменты данных сгруп- пированы вместе, вы можете подумать, что MASM будет вычислять смещения переменных относительно всей группы, в которой перемен- ные очевидно хранятся в памяти.
Но произойдет совсем не это! Вопреки вашим намерениям MASM вычисляет смещения переменных относительно отдельных сегментов dseg1, dseg2 и dseg3. Он делает это несмотря на то, что все три сегмента данных сгруппированы в памяти в один сегмент данных, ад- ресуемый через регистр DS. Бессмысленно определять смещения пере- менных относительно отдельных сегментов в тексте программы, ког- да эти сегменты скомбинированы в памяти в один сегмент. Единственный способ ссылки на такие переменные состоит в ссылке на их смещения относительно всей группы.

Чтобы избавиться в MASM от этой проблемы, вам потребуется наряду с ключевым словом OFFSET задавать имя группы:

mov ax,OFFSET DGROUP:v1 mov bx,OFFSET DGROUP:v2 mov cx,OFFSET DGROUP:v3

Хотя теперь это ассемблируется корректно и загружаются сме- щения переменных v1, v2 и v3 относительно DGROUP (где собраны от- дельные сегменты), вы можете легко забыть задать квалификатор DGROUP. Если вы сделаете эту ошибку, значения смещений не позво- лят корректно определить переменные в памяти, и вы не получите в MASM никакого указания, что что-то произошло не так. Режим Ideal позволяет избежать таких неприятностей:

IDEAL SEGMENT dseg1 para public 'data' v1 db 0 ENDS

SEGMENT dseg2 para public 'data' v2 db 0 ENDS

SEGMENT dseg3 para public 'data' v3 db 0 ENDS

GROUP DGROUP dseg1, dseg2, dseg3 SEGMENT cseg para public 'code'

ASSUME cs:cseg,ds:DGROUP

start: mov ax,OFFSET v1 mov bx,OFFSET v2 mov cx,OFFSET v3 ENDS END start

Смещения переменных v1, v2 и v3 корректно вычисляются отно- сительно группы, в которой собраны отдельные сегменты, которым принадлежат переменные. В режиме Ideal квалификатор DGROUP для ссылки на переменные в сегментах группы не требуется. В режиме MASM этот квалификатор также не является необходимым, но, что ху- же всего, не выдается никаких предупреждений, если вы забыли в конкретной ссылке определить имя группы.

Доступ к параметрам

Когда получает управление ваша подпрограмма на Турбо Ассемб- лере, вершина стека будет содержать адрес возврата (два или четы- ре слова, в зависимости от того, является ли подпрограмма ближней или дальней), а далее будут находится передаваемые параметры.
Примечание: При вычислении адресов параметров нужно принимать во внимание регистры, такие как BP, содержимое которых также может быть занесено в стек.)
Существует три основных метода доступа к параметрам, переда- ваемых Турбо Паскалем вашей подпрограмме на Турбо Ассемблере. Вы можете:
- использовать для адресации к стеку регистр BP;
- для получения параметров использовать другой базовый или индексный регистр;
- извлечь из стека адрес возврата, а затем параметры.
Первый и второй методы более сложны, и мы расскажем о них в следующих двух разделах. Третий метод предусматривает извлечение из стека и сохранение адреса возврата, а затем извлечения пара- метров и записи их в регистры. Лучше всего этот метод работает, когда ваша подпрограмма не требует пространства для локальных пе- ременных.

Duplicate dummy arguments:_

(Недопустимо использование одинаковых имен для формальных параметров)
В директиве MACRO определено несколько формальных параметров с одинаковыми именами. Например:
XYZ MACRO A,A ; ошибка: дублируются имена ; формальных параметров DB A ENDM
Все формальные параметры макроопределения должны иметь раз- личные имена.

ELSE or ENDIF without IF

(ELSE или ENDIF без IF)
Для директивы ELSE или ENDIF нет парной директивы IF, обоз- начающей начало условно ассемблируемого блока. Например:
BUF DB 10 DUP(?) ENDIF ; ошибка: нет парной директивы IFxxx

Error writing to listing file

(Ошибка при записи в файл листинга)
Возможно при записи файла листинга на диск было исчерпано место на этом диске.

Error writing to object file

(Ошибка при записи в объектный файл)
Возможно при записи объектного файла на диск было исчерпано место на этом диске.

Expecting METHOD keyword

(Требуется ключевое слово METHOD)
В расширенном структурном операторе для определения объектов после порождающего объекта требуется ключевое слово METHOD.

Expecting offset or pointer quantity

(Требуется указать смещение или указатель)
В качестве операнда выражения указано неверное значение вместо ожидаемого смещения внутри специфицированного сегмента. Например:
CODE SEGMENT mov ax,SEG CODE ; ошибка: СODE - это сегмент, ; а не адрес внутри сегмента CODE ENDS

Expecting offset quantity

(Требуется указать величину смещения)
В качестве операнда выражения указано неверное значение вместо ожидаемого смещения внутри сегмента. Например:
CODE SEGMENT mov ax,LOW CODE CODE ENDS

Expecting pointer type

(Операнд должен быть указателем)
Операндом текущей команды должен быть адрес памяти. Напри- мер:
LES di,4 ; неверно, т.к. 4 - константа

Expecting record field name

(Требуется имя поля записи)
Вы использовали инструкцию SETFIELD или GETFIELD без после- дующего имени поля.

Expecting register ID

(Требуется идентификатор регистра)
В части USES оператору CALL.METHOD требуются имена регист- ров.

Expecting scalar type

(Операнд должен быть константой)
Операндом текущей команды должна быть константа. Например:
BB DB 4 rol ax,BB ; ошибка: вторым операндом операции ROL ; должна быть константа

Expecting segment or group quantity

(Должно быть указано имя сегмента или группы)
В операторе вместо имени сегмента или группы указано имя, которое таковым не является. Например:
DATA SEGMENT ASSUME ds:F00 ; ошибка: F00 не является именем ; сегмента или группы F00 DW 0 DATA ENDS

Extra characters on line

(Лишние символы в строке)
Выражение имеет корректный синтаксис, однако вслед за ним, на той же строке имеются еще лишние символы. Например:
ABC = 4 shl 3 3 ; нет знака операции ; между двумя тройками
Данная ошибка обычно является следствием другой ошибки, в результате которой анализатор выражений преждевременно заканчива- ет обработку выражения.

Файл H2ASH.TSM

-----------------------------------------------------------------
Данный файл содержит подробную информацию об утилите-преоб- разователе файлов .h в .ahs. Краткое описание этой утилиты можно найти в Приложении D.

Файл HELPME!.TSM

На установочной дискете содержится также файл HELPME!.TSM. В нем можно найти ответы на вопросы, с которыми обычно сталкиваются пользователи. Он содержит также вопросы и ответы, касающиеся раз- личных режимов Турбо Ассемблера.

Файл README

Перед тем, как начать работу с Турбо Ассемблером, не за- будьте прочесть файл README. В нем находится последняя информация о программе, а также исправления и/или дополнения к руководствам. Там вы также найдете полный список всех поставляемых на дистрибу- тивных дисках файлов и их описания. В ответ на подсказку DOS вве- дите:
README
Вы можете также использовать программу README для чтения файлов документации (с расширением .DOC)

Файл TCREF.TSM

-----------------------------------------------------------------
В данном файле описывается использование для создании перек- рестных ссылок идентфикаторов и идентификации использующих их мо- дулей утилиты TCREF.

Файлы конфигурации

Турбо Ассемблер позволяет вам также поместить наиболее часто используемые параметры в файл конфигурации в текущем каталоге. Таким образом, когда вы запускаете Турбо Ассемблер, он будет в текущем каталоге искать файл TASM.CFG. При работе в операционной системе DOS 3.x и старше поиск будет также выполняться в катало- ге, из которого был загружен TASM или TASMX. Если Турбо Ассемблер находит этот файл, то он будет интерпретировать его, как косвен- ный файл, и обрабатывать его в командной строке первым.
Это может оказаться полезным, когда вы формируете "проект" программы, и все файлы проекта находятся в одном каталоге. При этом вы хотите, например, всегда выполнять ассемблирование с ис- пользованием эмулирования инструкций с плавающей точкой (пара- метр /E). Для этого вы можете поместить параметр в файл TASM.CFG, после чего его не нужно будет задавать каждый раз при запуске Турбо Ассемблера.
Содержимое файла конфигурации имеет тот же формат, что и косвенный файл. Этот файл может содержать любую допустимую в ко- мандной строке информацию и содержать столько строк, сколько не- обходимо. Параметры обрабатываются так, как если бы они содержа- лись на одной строке.
Содержимое файла конфигурации обрабатывается до всех других аргументов командной строки. Это позволяет вам отменить любой па- раметр, заданный в файле конфигурации, просто указав в командной строке параметр, который имеет противоположное действие. Напри- мер, если ваш файл конфигурации содержит параметры:
/A /E
и вы вызываете Турбо Ассемблер командой:
TASM /S /R MYFILE
где MYFILE - файл вашей программы, то ассемблирование будет вы- полнено с последовательным упорядочиванием сегментов (/S) и ре- альными инструкциями с плавающей точкой (/R), хотя в файле конфи- гурации содержатся директивы /A и /E, задающие упорядочивание сегментов по алфавитному порядку и эмулирование инструкций с пла- вающей точкой.
Назад | Содержание | Вперед

File not found

(Не найден файл)
В командной строке указано имя несуществующего исходного файла. Проверьте, правильно ли указано имя файла, включены ли имя диска и маршрут доступа, если файл находится в каталоге, отличном от текущего.

File was changed or deleted while assembly in progress

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

Формат фирмы Microsoft для двоичных чисел с плавающей точкой

В ранних версиях макроассемблера MASM для чисел с плавающей точкой по умолчанию использовался формат, несовместимый с форма- том для чисел с плавающей точкой стандарта IEEE. В MASM версии 5.1 числа с плавающей точкой генерируются в формате IEEE, но име- ется директива .MSFLOAT, с помощью которой можно специфицировать использование ранее использовавшегося формата фирмы Microsoft.
В Турбо Ассемблере не поддерживается старый формат чисел с плавающей точкой и, следовательно, .MSFLOAT является для него не- допустимой директивой.
Назад | Содержание | Вперед

Формат листинга

В верхней части каждой страницы листинга выводится заголо- вок, содержащий версию Турбо Ассемблера, с помощью которой выпол- нено ассемблирование файла, дату и время ассемблирования и номер страницы в листинге.
Файл листинга содержит две части: аннотированный листинг ис- ходного кода и таблицу идентификаторов. Сначала выводится исход- ный ассемблируемый код с заголовком, содержащим имя файла, в ко- тором находится исходный код. Вывод исходного кода Ассемблера сопровождается информацией о машинном коде, который ассемблирует Турбо Ассемблер. Все ошибки или предупреждения, обнаруженные в процессе ассемблирования, включаются в листинг непосредственно за содержащей ошибку строкой.
Строки кода в файле листинга имеет следующий формат:
<глубина> <номер_строки> <смещение> <машинный_код> <исходный_код>
где <глубина> показывает уровень вложенности включаемых файлов или макрокоманд в файле листинга.
Поле <номер_строки> содержит номер строки в файле листинга (не включая заголовки). Номера строк особенно полезны при исполь- зовании перекрестных ссылок Турбо Ассемблера, в которых указыва- ются номер строк. Учтите, что поле <номер_строки> не соответству- ет номерам строк исходного модуля. Например, если в файл включается другой файл или выполняется макрорасширение, то значе- ние поля <номер_строки> продолжает увеличиваться, хотя текущая строка в исходном файле остается той же. Чтобы преобразовать но- мер строки обратно в строку исходного кода, вы должны найти стро- ку в файле листинга, и найти такую же строку в исходном коде (по ее внешнему виду, а не по номеру).
Поле <смещение> представляет смещение в текущем сегменте на- чала машинного кода, сгенерированного из соответствующей исходной строки Турбо Ассемблером.
Поле <машинный_код> показывает фактическую последователь- ность шестнадцатиричных значений размером в байт или слово, кото- рые ассемблированы из соответствующей исходной строки на Ассемб- лере.
Поле <исходный_код> - это просто исходная строка Ассемблера, с комментариями и всем, что в ней содержится. Некоторые исходные строки (например те, которые содержат только комментарии) не ге- нерируют никакого машинного кода. Эти строки не содержат полей <смещение> и <машинный_код>, но имеют номер строки.

Forward reference needs override

(Ошибка при использовании умолчания для ссылки вперед)
В результате использования умолчания для ссылки вперед было зарезервировано меньше памяти, чем это оказалось необходимо. Эта ошибка возникает тогда, когда имя идентификатора, по умолчанию означающее адрес ближнего перехода или вызова, переопределяется в программе как дальний адрес. Эта ошибка возникает также в тех случаях, когда не указан сегмент (отличный от предполагаемого по умолчанию) для доступа к переменной. Например:
ASSUME cs:DATA call A ; по умолчанию - ближний вызов A PROC FAR ; а здесь переопределяется как дальний mov ax,MEMVAR ; не известно, что требуется другой сегмент DATA SEGMENT MEMVAR DW ? ; здесь ошибка: требуется переопределение
Эти ошибки исправляются путем явного указания сегмента или типа перехода FAR.

Начало работы на Турбо Ассемблере

Если вы никогда ранее не программировали на языке Ассембле- ра, то начните с данной главы. Возможно вам приходилось слышать, что программирование на Ассемблере - это дело темное, доступное только посвященным и мудрецам. Не верьте этому. Язык Ассемблера - это не более чем человеческая форма языка самого компьютера, а он, как и можно было предположить, в высшей степени логичен. Как вы можете также догадаться, язык Ассемблера - это очень мощный язык. Фактически, программирование на Ассемблере представляет со- бой единственный способ реализации всего спектра возможностей процессоров серии 80х86 фирмы Intel, являющихся "сердцем" всех компьютеров семейства IBM PC и совместимых с ними компьютеров.
Вы можете писать программы целиком на языке Ассемблера или, если захотите, использовать язык Ассемблера в программах, напи- санных на Турбо С++, Турбо Паскале и других языках. В любом слу- чае с помощью языка Ассемблера вы сможете разрабатывать компакт- ные и быстрые программы. Наряду со скоростью большое значение в программе на языке Ассемблера имеет также возможность управления всеми аспектами работы компьютера, до последнего такта системного таймера.
В данной главе вы познакомитесь с языком Ассемблера и опро- буете уникальные свойства программирования на нем. Сначала вы введете и запустите несколько работающих программ, написанных на Ассемблере. Это даст вам возможность как почувствовать сам язык, так и познакомиться с работой на Ассемблере.
Разумеется, мы не сможем при помощи этих нескольких глав сделать вас крупным специалистом по программированию на языке Ас- семблера; мы просто дадим вам введение в язык Ассемблера и помо- жем написать первые программы. Мы очень надеемся, что у вас есть хотя бы одна из множества замечательных книг, полностью посвящен- ных программированию на языке ассемблера и архитектуре PC (см. ссылки на литературу в конце данной книги). Кроме того, полезные справочные материалы содержатся в "Техническом справочнике по IBM DOS", "Техническом справочнике по интерфейсу BIOS" и "Техническом справочнике по персональным компьютерам XT"; в этих руководствах документируется интерфейс при программировании на языке ассембле- ра с программным и аппаратным обеспечением системы персональных компьютеров IBM.
Вы можете следовать данному руководству поэтапно, вводя все примеры программ по мере их изучения, либо можете распаковать на- ходящийся на диске файл с примерами (во время установки Турбо Ас- семблера) и иметь все эти программы под рукой. (Независимо от ва- шего решения, имена программ приводятся для удобства в самом начале примеров этих программ).

Описание процедур

Турбо Ассемблер позволяет вам описывать процедуры нескольки- ми способами. В данной главе описываются процедуры NEAR и FAR, объявление языка процедур, использование в процедурах аргументов и переменных, сохранение регистров, вложенные процедуры и описа- ние процедур методов для объектов.

Управление областью действия идентификаторов

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

Определение данных

Директивы определения данных используются для выделения байт в сегменте. Вы можете также использовать их для заполнения этих байт начальными данными и определения переменных данных.
Все директивы определения данных имеют некоторые общие средства. Во первых они могут генерировать инициализированные данные и резервировать место для неинициализированных данных. Для инициализированных данных определяется некоторое начальное значе- ние. Неинициализированные данные определяются без задания началь- ного значения (говорят, что их начальное значение является неоп- ределенным). В директивах определения данных неинициализированные данные указываются с помощью символа ?. Все прочее должно предс- тавлять значение инициализированных данных. О том, как следует различать инициализированные и неинициализированные данные, расс- казывается в Главе 7.
Другим средством, которое применяется во всех директивах определения данных, является использование для указания повторе- ния блока данных ключевого слова DUP. Приведем общий синтаксис всех директив распределения данных:
[имя_директивы [выражение_dup [выражение_dup.]]
Турбо Ассемблер инициализирует "имя" таким образом, чтобы оно указывало на резервируемую директивой область. Тип данной пе- ременной зависит от фактически используемой директивы.
Каждое "выражение_dup" может иметь следующий синтаксис:
- ?;
- значение;
- выражение_счетчик DUP( выражение_dup[,выражение_dup.]);
где "выражение_счетчик" задает, сколько раз будет повторяться блок данных. "Выражение_счетчик" не может быть относительным и иметь опережающие ссылки.
Если вы хотите получить неинициализированные данные, исполь- зуйте идентификатор ?. Объем резервируемой для неиницализирован- ных данных памяти зависит от фактически используемой директивы.
"Значение" предназначено для фактического описания отдельно- го элемента данных в каждой директиве. В некоторых директивах поле значения может быть очень сложным и содержать много элемен- тов, другие могут потребовать только простого выражения.
В следующем примере используется директива DW, которая выде- ляет слова:
DW 2 DUP (3 DUP (1,3),5) ; эквивалентно директиве ; DS 1,3,1,3,1,35,1,3,1,3,1,3,5

Расширенные инструкции

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

Использование макрокоманд

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

Использование условных директив

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

Интерфейс с компоновщиком

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

Генерация листинга

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

Интерфейс Турбо Ассемблера и Borland C++

Хотя некоторые программисты могут разрабатывать программы целиком на языке Ассемблера (и делают это), другие предпочитают писать основную часть программы на языке высокого уровня, обраща- ясь к языку Ассемблера только для осуществления управления нижне- го уровня, или когда требуется высокая производительность. Неко- торые предпочитают писать преимущественно на Ассемблере, только иногда используя конструкции и библиотечные средства языков высо- кого уровня.
Данная глава объясняет использование Турбо Ассемблера с ком- пиляторами С++. В этой главе они называются компиляторами семейс- тва Borland С++. Однако Турбо Ассемблер можно использовать также с Турбо C++ и Турбо Си. Приводимая ниже таблица содержит перечень компиляторов этого семейства.
Компиляторы Borland С++ и Си Таблица 18.1 ---------------------T------------------------------------------¬ ¦ Название продукта ¦ Имя файла компилятора ¦ +--------------------+------------------------------------------+ ¦ Borland С++ ¦ bcc.exe, bccx.exe, bc.exe или bcx.exe ¦ ¦ Турбо C++ ¦ tcc.exe или tc.exe ¦ ¦ Турбо Cи ¦ tcc.exe ¦ L--------------------+-------------------------------------------
Например, если мы говорим, чтобы вы выполнили компиляцию строкой:
bcc -S plusone.cpp
а вы работаете с Турбо С++, то вместо нее вы можете ввести следу- ющую командную строку:
tcc -S plusone.cpp
Если при интерфейсе с Турбо Ассемблером вы используете Турбо Си, то вы ограничены использованием только компилятора командной строки. В случае же Borland С++ и Турбо С++ вы можете работать как с компиляторами командной строки, так и с компилятором интег- рированной среды.
Borland C++ дает хорошие возможности поддержки смешанного программирования на С++ и на языке Ассемблера на произвольной ос- нове и предоставляет не один, а целых три механизма объединения модулей на Ассемблере и на С++. Имеющееся в С++ средство встроен- ного ассемблирования позволяет быстро и просто вставить текст на языке Ассемблера прямо в функцию С++. Вы можете выполнить ассемб- лирование встроенного кода при помощи Турбо Ассемблера или встро- енного Ассемблера Borland С++. Те, кто предпочитает держать ас- семблерные части программы в отдельных модулях, написанных цели- ком на языке Ассемблера, может ассемблировать их при помощи Турбо Ассемблера и затем скомпоновать с модулями Borland С++.
Сначала мы рассмотрим использование встроенного в Borland C++ Ассемблера. Далее мы подробно обсудим компоновку отдельно ас- семблированных в Турбо Ассемблере модулей с Borland C++ и иссле- дуем процесс вызова функций, написанных с помощью Турбо Ассембле- ра, из программы, созданной в Borland C++. И наконец, мы рассмот- рим вызов функций Borland C++ из программы на Турбо Ассемблере.

Интерфейс Турбо Ассемблера с Турбо Паскалем

В Турбо Ассемблере предусмотрены расширенные и мощные средства, позволяющие вам добавлять код Ассемблера к программам Турбо Паскаля. В данной главе мы подробно расскажем вам о том, что нужно знать, чтобы полностью использовать данные средства, приведем множество примеров и дадим некоторую более глубокую ин- формацию.
Большинство программ, которые вы захотите написать, можно реализовать целиком на Турбо Паскале. В отличие от большинства других компиляторов Паскаля, Турбо Паскаль позволяет вам с по- мощью массивов Port[], Mem[], MemW[] и MemL[] непосредственно об- ращаться ко всем ресурсам компьютера, а с помощью процедур Intr() и MsDos() вы можете обращаться к базовой системе ввода-вы- вода (BIOS) и операционной системе.
Для чего же тогда может потребоваться использовать совместно с Турбо Паскалем Ассемблер? Для этого существуют две вероятные причины: выполнение некоторого небольшого числа операций, которые непосредственно в Турбо Паскале недоступны, и использование преи- муществ высокой скорости работы, которые дает Ассемблер. (Сам Турбо Паскаль работает достаточно быстро, потому что он написан на языке Ассемблера.) В данной главе мы покажем вам, как можно использовать в Турбо Паскале преимущества Ассемблера.

Использование директив и параметров

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

Общие принципы программирования

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

Объектно-ориентированное программирование

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

Использование выражений и значений идентификаторов

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

Директивы выбора процессора и идентификаторы процессора

Процессор 8086 представляет собой на самом деле только один из процессоров семейства iAPx86. Это семейство включает в себя:
- процессор 8088 (который содержит 8-разрядную шину данных), 8086 (содержит 16-разрядную шину данных);
- процессоры 80186 и 80188 (аналогичны процессорам 8086 и 8088, но содержат дополнительные инструкции и работают быстрее своих предшественников);
- процессор 80286 (который содержит инструкции защищенного режима);
- процессор 80386 (который может обрабатывать 16- и 32-раз- рядные данные);
- процессор 80486 (улучшенная версия процессора 80386, кото- рая отличается более высокой скоростью работы).
Математические сопроцессоры, такие как 8087, 80287 и 80387, работающие с процессорами семейства iAPx86, позволяют выполнять операции с плавающей точкой.
В Турбо Ассемблере предусмотрены директивы и предопределен- ные идентификаторы, которые позволяют вам использовать инструкции конкретного процессора. Эти директивы и идентификаторы описывают- ся в данной главе.

Использование моделей памяти программы и сегментации

Каждый процессор семейства 80х86 имеет не менее 4 сегментных регистров (CS, DS, ES и SS). Эти регистры содержат значение сег- мента, которое описывает физический блок памяти объемом до 64К (или до 4 гигабайт в процессоре 80386 и старше). Все адреса вы- числяются с использованием в качестве базового значения одного из сегментных регистров.
Смысл значения записанного в сегментном регистре бывает раз- личным и зависит от используемого в процессоре режиме адресации - реального (это единственный режим, который может использоваться в процессорах 8086 и 80186), когда сегментное значение представляет собой фактический номер параграфа, или защищенного режима, когда сегментный регистр содержит селектор (не имеющий числового значе- ния).
Операционная система или среда программы определяет, работа- ет программа в реальном или защищенном режиме. Если вы используе- те защищенный режим процессоров 80386 или 80486, то операционная система определяет также, допустимы ли большие сегменты (до 4 ги- габайт). Турбо Ассемблер в одинаковой степени поддерживает все эти операционные среды.
В случае общей модели 80х86 программы состоят из одного или более сегментов, где каждый сегмент представляет собой физически различные части кода или данных (или код и данные), к которым можно обращаться через сегментный регистр. На основе этой общей схемы можно построить много производных схем. Чтобы упорядочить их, разработаны стандартные схемы памяти. Так как этих соглашений придерживаются многие языки высокого уровня, в программах на Ас- семблере также следует это делать.
Один из очевидных способов состоит в разбиении программы на инструкции программы и ее данные. Каждый фрагмент данных програм- мы можно определить как инициализированный (содержащий начальные значения, например, текстовое сообщение) и неинициализированный (не содержащий начального значения). Турбо Ассемблер обычно прис- ваивает неинициализированным данным отдельный сегмент, который можно разместить в конце программы, уменьшив объем выполняемой программы.
Обычно достаточно большую часть неинициализированных данных занимает стек. Он также специализирован, так как регистры SS и SP при выполнении программы инициализируются обычно автоматически областью стека. Таким образом, в стандартных моделях памяти стек инициализируется, как отдельный сегмент.
Можно также комбинировать сегменты в группы. Преимущество использования групп состоит в том, что вы можете задать сегмент- ное значение для всех сегментов группы. Например, инициализиро- ванные данные, неинициализированные данные и сегменты стека часто комбинируются в группы таким образом, что для всех данных прог- раммы можно использовать одно и то же значение сегмента.
В данной главе описывается, как использовать в вашей прог- рамме модели и сегменты, и какие директивы позволяют это делать.

Определение типов данных

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

Задание и использование счетчика адреса

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

Global type doesn't match symbol type

(Тип, указанный в директиве GLOBAL не совпадает с действи- тельным типом имени идентификатора)
Это предупреждающее сообщение выдается, если объявление сим- волического имени директивой GLOBAL находится в том же самом сег- менте, где определяется это имя идентификатора, однако тип, ука- занный в директиве GLOBAL, не совпадает с действительным типом этого имени идентификатора.

Грамматика выражений в режиме Ideal

Синтаксис начинается, как id_выражение.
id_выражение ::=
указатель
i_тип ::= UNKNOWN BYTE WORD DWORD FWORD QWORD PWORD TBYTE SHORT NEAR FAR PROC DATAPTR CODEPTR имя_структуры имя_таблицы имя_перечисления имя_записи TYPE указатель
указатель ::= SMALL указатель ; если 386 LARGE указатель ; если 386 i_тип PTR указатель i_тип LOW указатель i_тип HIGH указатель i_тип указатель указатель_2
указатель_2 ::= указатель_3.идентификатор (Операция выбора элемента структуры) указатель_3
указатель_3 ::= выражение:указатель_3 выражение
выражение ::= SYMTYPE выражение (Операция определения типа символическо- го имени) выражение_2
выражение_2 ::= выражение_3 OR выражение_3. выражение_3 ХOR выражение_3. выражение_3
выражение_3 ::= выражение_4 AND выражение_4. выражение_4
выражение_4 ::= NOT выражение_4 выражение_5
выражение_5 ::= выражение_6 ЕQ выражение_6. выражение_6 NE выражение_6. выражение_6 LT выражение_6. выражение_6 LE выражение_6. выражение_6 GT выражение_6. выражение_6 GE выражение_6. выражение_6
выражение_6 ::= выражение_7 + выражение_7. выражение_7 - выражение_7. выражение_7
выражение_7 ::= выражение_8 * выражение_8. выражение_8 / выражение_8. выражение_8 MOD выражение_8. выражение_8 SHR выражение_8. выражение_8 SHL выражение_8. выражение_8
выражение_8 +выражение_8 -выражение_8 выражение_9
выражение_9 HIGH выражение_9 LOW выражение_9 выражение_10
выражение_10 OFFSET указатель SEG указатель SIZE идентификатор LENGTH идентификатор WIDTH идентификатор MASK идентификатор THIS идентификатор идентификатор константа ( указатель ) [ указатель ] (Всегда означает "содержимое")

Грамматика выражений в режиме MASM

Синтаксис выражений начинается как MASM_выражение.
MASM_выражение ::= выражение_1
м_выражение_1 ::= SHORT м_выражение_1 .TYPE м_выражение_1 SMALL м_выражение_1 (преобразование в 16-битовое смещение [только для процессора 386]) LARGE м_выражение_1 (преобразование в 32-битовое смещение [только для процессора 386]) м_выражение_2
м_выражение_2 ::= м_выражение_3 OR м_выражение_3. м_выражение_3 ХOR м_выражение_3. м_выражение_3
м_выражение_3 ::= м_выражение_4 AND м_выражение_4. м_выражение_4
м_выражение_4 ::= NOT м_выражение_4 м_выражение_5
м_выражение_5 ::= м_выражение_6 'EQ' м_выражение_6. м_выражение_6 'NE' м_выражение_6. м_выражение_6 'LT' м_выражение_6. м_выражение_6 'LE' м_выражение_6. м_выражение_6 'GT' м_выражение_6. м_выражение_6 'GE' м_выражение_6. м_выражение_6
м_выражение_6 ::= м_выражение_7 '+' м_выражение_7. м_выражение_7 '-' м_выражение_7. м_выражение_7
м_выражение_7 ::= м_выражение_8 '*' м_выражение_8. м_выражение_8 '/' м_выражение_8. м_выражение_8 'MOD' м_выражение_8. м_выражение_8 'SHR' м_выражение_8. м_выражение_8 'SHL' м_выражение_8. м_выражение_8
м_выражение_8 ::= + выражение_8 - выражение_8 выражение_12
м_выражение_10 ::= OFFSET указатель SEG указатель SIZE идентификатор LENGTH идентификатор WIDTH идентификатор MASK идентификатор THIS цел_тип идентификатор ( указатель ) [ указатель ]
м_выражение_10 ::= м_выражение_11 PTR м_выражение_10 м_выражение_11 OFFSET м_выражение_10 SEG м_выражение_10 LOW м_выражение_10 HIGH м_выражение_10 TYPE м_выражение_10 THIS м_выражение_10
м_выражение_11 ::= м_выражение_8 : м_выражение_8.
м_выражение_12 ::= м_выражение_13 [м_выражение_13.(подразумеваемое сложение, только если есть '[' или '(') м_выражение_13 (м_выражение_13.(подразумеваемое сложение, только если есть '[' или '(') м_выражение_13'.'м_выражение_10
м_выражение_13 ::= LENGTH идентификатор SIZE идентификатор WIDTH идентификатор MASK идентификатор ( м_выражение_1 ) [ м_выражение_1 ] м_выражение_10

ID not member of structure

(Идентификатор не является полем структуры)
В режиме Ideal после точки (селектора поля структуры) указа- но имя идентификатора, которое не является именем поля структуры. Например:
IDEAL STRUC DEMO DB ? ENDS COUNT DW 0 mov ax,[(DEMO bx).COUNT] ; COUNT не является ; полем структуры
После точки может быть указано только имя поля той структу- ры, имя которой указано перед точкой.
Данная ошибка обычно является следствием другой ошибки, в результате которой анализатор выражений преждевременно заканчива- ет обработку выражения.

Идентификатор @32Bit

Идентификатор @32Bit содержит значение, определяющее, явля- ются ли сегменты в текущей модели 16- или 32-разрядными. Если вы задали в модели MODEL 16-разрядные сегменты, то это значение рав- но 0, а если 32-разрядные - то 1.

Идентификатор @CodeSize

Идентификатор @CodeSize указывает используемый по умолчанию размер указателя кода в текущей модели памяти. Если он установлен в 0, то модели памяти используют ближние указатели кода типа NEAR (модeли TINY, SMALL, FLAT, COMPACT, TPASCAL), а значение 1 указы- вает, что модели памяти используют дальние указатели FAR (все другие модели).

Идентификатор @Cpu

Функция: Числовое значение, возвращающее информацию о теку- щем процессоре.
Замечания: Значение, возвращаемое @Cpu, дает в битовых полях информацию о типе процессора:
------------T--------------------------------------¬ ¦ Бит ¦ Описание ¦ +-----------+--------------------------------------+ ¦ 0 ¦ Разрешены инструкции процессора ¦ ¦ ¦ 8086. ¦ ¦ ¦ ¦ ¦ 1 ¦ Разрешены инструкции процессора ¦ ¦ ¦ 80186. ¦ ¦ ¦ ¦ ¦ 2 ¦ Разрешены инструкции процессора ¦ ¦ ¦ 80286. ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ 3 ¦ Разрешены инструкции процессора ¦ ¦ ¦ 80386. ¦ ¦ ¦ ¦ ¦ 4 ¦ Разрешены инструкции процессора ¦ ¦ ¦ i486. ¦ ¦ ¦ ¦ ¦ 7 ¦ Разрешены привилегированные ¦ ¦ ¦ инструкции процессоров (80286, ¦ ¦ ¦ 386, 486). ¦ ¦ ¦ ¦ ¦ 8 ¦ Разрешены инструкции математи- ¦ ¦ ¦ ческого сопроцессора 8087. ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ 10 ¦ Разрешены инструкции математи- ¦ ¦ ¦ ческого сопроцессора 80287. ¦ ¦ ¦ ¦ ¦ 11 ¦ Разрешены инструкции математи- ¦ ¦ ¦ ческого сопроцессора 80387. ¦ L-----------+---------------------------------------
Не определенные здесь биты зарезервированы для будущего ис- пользования. При использовании идентификатора @Cpu замаскируйте их, это обеспечит совместимость вашей программы с будущими верси- ями Турбо Ассемблера.
Так как семейство процессоров 8086 совместимо снизу-вверх, то когда вы разрешаете тип процессора по директиве .286, автома- тически разрешаются также младшие типы сопроцессоров (8086, 80186).
Это значение только предоставляет информацию о процессоре, который вы выбрали на этапе ассемблирования с помощью .286 или аналогичных директив. Тип процессора, на котором выполняется ваша программа, не указывается.
Пример:
IPUSH = @Cpu AND 2 ; разрешает непосредственную ; инструкцию push процессора ; 186 и старше IF IPUSH PUSH 1234 ELSE mov ax,1234 push ax ENDIF

Идентификатор @DataSize

Идентификатор @DataSize указывает используемый по умолчанию размер указателя данных в текущей модели памяти. Если он установ- лен в 0, то модели памяти используют ближние указатели данных ти- па NEAR (модeли TINY, SMALL, FLAT, COMPACT, TPASCAL), а значение 1 указывает, что модели памяти используют дальние указатели FAR (все другие модели).

Идентификатор @Interface

Идентификатор @Interface дает информацию о языке и операци- онной системе, которые выбраны в операторе MODEL. Данная тексто- вая макрокоманда содержит число, биты которого представляют сле- дующие значения:
Модификаторы модели Таблицы 7.3 -----------------T----------------------------------------------¬ ¦ Биты 0-7 ¦ Значение ¦ +----------------+----------------------------------------------+ ¦ 0 ¦ NOLANGUAGE ¦ ¦ 1 ¦ C ¦ ¦ 2 ¦ SYSCALL ¦ ¦ 4 ¦ PASCAL ¦ ¦ 5 ¦ FORTRAN ¦ ¦ 6 ¦ BASIC ¦ ¦ 7 ¦ PROLOG ¦ ¦ 8 ¦ CPP ¦ L----------------+-----------------------------------------------
Бит 8 имеет нулевое значение для DOS и равен 1 для OS/2.
Например, значение идентификатора @Interface показывает, что вы выбрали операционную систему OS/2 и язык Си.

Идентификатор @Model

Идентификатор @Model содержит числовое значение, представля- ющее текущую используемую модель. Вы можете задавать его как текстовую макрокоманду с одним из следующих значений:
1 = действует модель tiny (крошечная) 2 = действует модель small (малая) или flat 3 = модель compact (компактная) 4 = medium (средняя) 5 = large (большая) 6 = huge (огромная) 7 = tchuge (Турбо Си) 0 = tpascal (Турбо Паскаль)

Идентификатор счетчика адреса $

Предопределенный идентификатор $ представляет текущий счет- чик адреса. Счетчик адреса состоит из двух частей: сегмента и смещения. При ассемблировании счетчик адреса представляет собой смещение в текущем сегменте.
Счетчик адреса - это адрес адрес, который увеличивается и отражает текущий адрес при ассемблировании каждого оператора ис- ходного файла. Например:
helpkMessage DB 'это справочное сообщение программы' helpLength = $ - helpMessage
Когда эти две строки ассемблируются, идентификатор helpLength равен длине справочного сообщения.

Идентификатор @Startup

Идентификатор @Startup помещается в начало кода инициализа- ции, который генерируется по директиве STARTUPCODE. Он представ- ляет собой ближнюю метку, отмечающую начало программы.

Идентификатор @WordSize

Функция: Числовое значение, указывающее 16- или 32-разрядные сегменты.
Примечания: @WordSize возвращает значение 2, если текущим сегментом является 16-разрядный сегмент, и 4, если это 32-разряд- ный сегмент.
Пример:
IF @WordSize EQ 4 mov esp,0100h ELSE mov sp,0100h ENDIF

Идентификаторы, генерируемые директивой MODEL

Когда вы используете директиву MODEL, Турбо Ассемблер созда- ет и инициализирует отдельные переменные, отражающие детали выб- ранной модели. Эти переменные путем использования операторов ус- ловного ассемблирования могут вам помочь написать код, зависящий от модели. О том, как можно использовать эти переменные для изме- нения процесса ассемблирования, рассказывается в Главе 15.

Идентификаторы, определяемые расширенной директивой STRUC

Расширенная директива STRUC определяет и использует несколь- ко идентификаторов, которые отражают определяемый объект. Эти идентификаторы показаны в следующей таблице:
Идентификаторы, определяемые расширенной директивой STRUC Таблица 8.4 ---------------------------T------------------------------------¬ ¦ Идентификатор ¦ Значение ¦ +--------------------------+------------------------------------+ ¦ @Object ¦ Текстовая макрокоманда, содержащая¦ ¦ ¦ текущий объект. ¦ ¦ ¦ ¦ ¦ @Table_<имя_объекта> ¦ Табличный тип данных, содержащий¦ ¦ ¦ таблицу методов объекта. ¦ ¦ ¦ ¦ ¦ @Tableaddr_<имя_объекта>¦ Метка, описывающая адрес таблицы¦ ¦ ¦ виртуальных методов объекта. ¦ L--------------------------+-------------------------------------
Назад | Содержание | Вперед

Идентификаторы, создаваемые упрощенными директивами сегментации

Когда вы используете упрощенные директивы определения сег- ментов, они создают переменные, отражающие детали распределения сегмента (аналогично тому, как это делает директива MODEL). Под- робнее об этом рассказывается в Главе 15.
Идентификаторы упрощенных директив сегментации Таблица 7.5 ----------------------------T-----------------------------------¬ ¦ Идентификатор ¦ Значение ¦ +---------------------------+-----------------------------------+ ¦ @code ¦ Сегмент или группа, которые подра-¦ ¦ ¦ зумеваются для сегмента CS. ¦ ¦ ¦ ¦ ¦ @data ¦ Сегмент или группа, которые подра-¦ ¦ ¦ зумеваются для сегмента DS. ¦ ¦ ¦ ¦ ¦ @fardata ¦ Имя текущего сегмента FARDATA. ¦ ¦ ¦ ¦ ¦ @fardata? ¦ Имя текущего сегмента UFARDATA. ¦ ¦ ¦ ¦ ¦ @curseg ¦ Имя текущего сегмента. ¦ ¦ ¦ ¦ ¦ @stack ¦ Сегмент или группа, которые подра-¦ ¦ ¦ зумеваются для сегмента SS. ¦ L---------------------------+------------------------------------

Идентификаторы в выражениях

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

Идентификаторы

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

Illegal forward reference

(Недопустимая ссылка вперед)
Имя идентификатора, на которое происходит ссылка, еще не бы- ло определено, а в соответствующей директиве не допускается ис- пользовать ссылки вперед. Например:
IF MYSYM ; ошибка: MYSYM еще не определена ; ENDIF MYSYM EQU 1
Ссылки вперед нельзя использовать в директивах вида IFxxx, а также в качестве счетчика выражений в операции DUP.

Illegal immediate

(Недопустим непосредственный операнд)
Команда содержит непосредственный операнд (т.е. константу) там, где он недопустим. Например:
mov 4,al

Illegal indexing mode

(Недопустимый режим индексации)
Команда содержит операнд, использующий недопустимую комбина- цию регистров. Например:
mov al,[si+ax]
Для всех процессоров, за исключением 80386, допустимыми ком- бинациями индексных регистров являются следующие: BX, BP, SI, DI, BX+SI, BX+DI, BP+SI, BP+DI.

Illegal instruction for currently selected processor(s)

(Недопустимая команда для выбранного в настоящий момент про- цессора (процессоров))
Строка программы содержит команду, которая не может быть ас- семблирована текущим процессором. Например:
.8086 PUSH 1234h ; занесение в стек непосредственного ; операнда для процессора 8086 не ; допускается
При ассемблировании исходного файла с помощью Турбо Ассемб- лера по умолчанию будут разрешены команды процессора 8086. Если желательно использовать расширенные мнемоники команд, определен- ные для процессоров 186/286/386, то должна быть указана одна из директив, разрешающих использование этих команд (Р186, Р286, Р386).

Illegal instruction

(Недопустимая команда)
Строка исходной программы начинается с имени идентификатора, которое не является ни именем директивы, ни мнемоникой команды. Например:
move ax,4 ; должно быть "MOV"

Illegal local argument

(Недопустимый локальный параметр)
В директиве LOCAL внутри макроопределения указан параметр, не являющийся допустимым именем идентификатора. Например:
X MACRO LOCAL 123 ; это не идентификатор ENDM

Illegal local symbol prefix

(Недопустимый префикс для локальных имен идентификаторов)
В директиве LOCALS специфицируется недопустимый префикс для локальных имен идентификаторов. Например:
LOCALS XYZ ; ошибка: допускается только 2 символа
Префикс локальных идентификаторов должен состоять ровно из двух символов и представлять собой допустимое имя идентификатора. Например, допустимыми являются следующие комбинации: __, @@ (по умолчанию для локальных имен идентификаторов используется префикс @ @).

Illegal mаcro argument

(Недопустимый параметр макрокоманды)
В директиве MACRO для формального параметра макрокоманды указано недопустимое имя идентификатора. Например:
X MACRO 123 ; недопустимый формальный параметр ENDM

Illegal memory reference

(Недопустима ссылка на память)
Операнд команды содержит ссылку на адрес памяти, что недо- пустимо для данного операнда. Например:
mov [bx],BYTE PTR A ; ошибка: недопустима пересылка ; из памяти в память
В данной команде оба операнда представляют собой адреса па- мяти, что недопустимо в команде mov. В командах семейства процес- соров 80х86 только один из операндов может ссылаться на адрес па- мяти.

Illegal number

(Недопустимое число)
Число содержит один или более символов, недопустимых для чи- сел данного формата. Например:
Z = 0ABCGH
Здесь G - недопустимый символ для шестнадцатиричных чисел.

Illegal origin address

(Недопустимый начальный адрес)
Указан адрес, недопустимый для установки текущего сегментно- го адреса ($). Допускается указывать константу, выражение, ис- пользующее счетчика адреса ($), или имя идентификатора из текуще- го сегмента.

Illegal override in structure

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

Illegal override register

(Недопустимое переопределение регистра)
В ссылке на сегмент перед двоеточием был указан регистр, не являющийся сегментным регистром. К сегментным регистрам относятся следующие регистры: CS, DS, ES, SS, а также FS и GS для процессо- ра 80386. Например:
mov dx:XYZ,1 ; DX не является сегментным регистром

Illegal radix

(Недопустимое основание системы счисления)
В директиве .RADIX в качестве основания системы счисления указано недопустимое число. Например:
.RADIX 7 ; неверно
Основанием системы счисления могут быть следующие числа: 2, 8, 10 и 16. Это число интерпретируется как десятичное, независимо от текущего умолчания для основания системы счисления.

Illegal register for instruction

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

Illegal register multiplier

(Недопустимо указание множителя для регистра)
Попытка умножить регистр на какое-либо число, что является недопустимой операцией. Например:
mov ax * 3,1
Единственный случай, когда допускается умножение регистра на константное выражение - это при определении масштабного индексно- го операнда для процессора 80386.

Illegal segment address

(Недопустимый сегментный адрес)
Это сообщение об ошибке выдается, если для позиционирования сегмента указывается значение адреса, превышающее 65535. Напри- мер:
F00 SEGMENT AT 12345h

Illegal use of constant

(Недопустимо использование константы)
В выражении используется константа там, где использование константы недопустимо. Например:
mov bx+4,5

Illegal use of register

(Недопустимо использование регистра)
В выражении указывается имя регистра там, где использование регистра недопустимо. Например:
X = 4 shl ax ; в операции SHL нельзя указывать регистры

Illegal use of segment register

(Недопустимо использование сегментного регистра)
В выражении или команде указывается имя сегментного регистра там, где использование сегментного регистра недопустимо. Напри- мер:
ADD SS,4 ; в команде ADD нельзя указывать ; сегментные регистры

Illegal USES register

(В директиве USES указан недопустимый регистр)
Указан недопустимый регистр в командах PUSH и POP при входе и выходе из процедуры. Допустимы следующие регистры:
AX CX DS ES BX DI DX SI
Если директивой .386 или .386Р разрешены команды процессора 80386, то можно указывать 32-битовые эквиваленты перечисленных выше сегментов.

Illegal version ID

(Недопустимый идентификатор версии)
Эта ошибка происходит, когда в параметре /U или в операторе VERSION выбирается недопустимая версия.

Illegal warning ID

(Недопустимый идентификатор предупреждающего сообщения)
Введен неверный трехсимвольный идентификатор предупреждающе- го сообщения. Список всех допустимых идентификаторов предупрежда- ющих сообщений приведен в Главе 2.

Имена идентификаторов

Имена идентификаторов представляют собой сочетание букв (в верхнем и нижнем регистре), цифр и специальных символов. Имена идентификаторов не могут начинаться с цифры. Турбо Ассемблер мо- жет интерпретировать имена идентификаторов с различием регистра символов или без него. Различимостью регистра символов можно уп- равлять с помощью параметров командной строки Турбо Ассемблера /ML, /MU и /MX.
Имена идентификаторов могут иметь в длину до 255 символов. По умолчанию имена идентификаторов являются значащими до 32 сим- волов. Для изменения числа значащих символов в имени идентифика- тора вы можете использовать параметр командной строки /MV.
Примечание: Об использовании параметров командной строки рассказывается в Главе 2.
В имени идентификатора можно использовать символ подчеркива- ния (_), знак вопроса (?), знак доллара ($) и символ @. В режиме MASM (и только в нем) в качестве первого символа имени вы можете использовать точку (.). Однако легко спутать точку в начале иден- тификатора с операцией точки (которая используется для выделения элемента структуры), поэтому лучше не использовать ее в именах идентификаторов.

Информационные сообщения

Турбо Ассемблер обычно выводит два информационных сообщения. Во-первых, всегда выдается информационное сообщение непосредс- твенно перед началом ассемблирования пользовательского исходного файла (или файлов). Кроме того, выдается информационное сообщение по окончании ассемблирования каждого файла. Пример информационно- го сообщения, выдаваемого при запуске Турбо Ассемблера:
Turbo Assembler Version 3.0 Copyright(C) 1991 Borland International Assembling file: TEST.ASM
(Турбо Ассемблер, версия 3.0, продукт фирмы Borland, 1991г.)
По окончании ассемблирования исходного файла выдается сооб- щение с краткой характеристикой процесса ассемблирования. Это со- общение выглядит следующим образом:
Error messages: None Warning messages: None Passes: 1 Remaining memory: 279K
(Сообщения об ошибках: Нет Проходов: 1 Предупреждающие сообщения: Нет Осталось памяти: 279Кб)
Подавить все информационные сообщения можно с помощью пара- метра командной строки /T. Информационные сообщения подавляются только в случае отсутствия ошибок в процессе ассемблирования. При наличии ошибок параметр /T никакого влияния не имеет: стандартные информационные сообщения будут выданы в начале и в конце процесса ассемблирования.

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

Вы можете использовать любое выражение, при вычисление кото- рого получается число, умещающееся в экземпляре перечислимого ти- па данных, например:
ETYPE ? ; неинициализированный экземпляр ETYPE FOO ; инициализированный экземпляр, ; значение FOO ETYPE 255 ; число вне ENUM также подходит

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

Инициализированные экземпляры структуры несколько более сложны, чем неинициализированные. Когда вы определяете структуру, вам нужно определить используемое по умолчанию начальное значение для каждого ее элемента. (В качестве начального значения можно использовать ключевое слово ?, которое указывает, что конкретного начального значения сохранять не следует.) Когда вы создаете эк- земпляр структуры, его можно создать с использованием значений по умолчанию или переопределяемых значений. Простейший инициализиро- ванный экземпляр структуры содержит просто заданные в определении начальные значения. Например:
ASTRUC {}
что эквивалентно следующему:
DB "xyz" DW 1 DD 2
Фигурные скобки ({}) представляет нулевой инициализатор для структуры. Значение-инициализатор определяет, что элементы (если они имеются) должны иметь начальные значения, которые следует пе- реопределить, и какими новыми значениями, если вы выделяете дан- ные для экземпляра структуры. Инициализатор в виде фигурных ско- бок имеет следующий синтаксис:
{ [имя_элемента = значение [,имя_элемента.]] }
где "имя_элемента" - это имя элемента структуры или объединения, а "значение" - это то значение, которое вы хотите присвоить эле- менту в данном экземпляре. Чтобы указать Турбо Ассемблеру, что для элемента нужно использовать начальное значение из определения структуры или объединения, задайте нулевое значение. Значение ? показывает, что этот элемент инициализировать не нужно. Турбо Ас- семблер присваивает всем элементам, которые не указываются в ини- циализаторе, начальное значение из определения структуры или объ- единения, например:
ASTRUC {C=2,D=?}
эквивалентно следующему:
DB "xyz" DW 2 DD ?
Вы можете использовать инициализатор в виде фигурных скобок для задания значения любого элемента структуры или объединения, даже если он находится во вложенной структуре или объединении.
Объединения отличаются от структур, поскольку элементы в объединении перекрывают друг друга. Будьте аккуратны, когда вы инициализируете экземпляр структуры, поскольку если несколько элементов перекрывают друг друга, Турбо Ассемблер допускает, что- бы в экземпляре только один элемент имел инициализирующее значе- ние. Например:

BUNION {}

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

BD 4 DUP (?)

В данном примере 4 байта зарезервированы, поскольку размер объединения равен размеру его наибольшего элемента (в данном слу- чае DWORD). Если инициализированный элемент объединения не явля- ется наибольшим элементом объединения, то Турбо Ассемблер воспол- няет разницу, резервируя место, но не генерируя данные, например:

BUNION {Z=1}

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

DB 1 DB 3 DUP (?)

Наконец, несколько инициализированных элементов объединения приведут к ошибке. Следующее, например, не допускается:

BUNION {X=1,Z=2}

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

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

< [значение [,значение.]] >

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

ASTRUC <"ABC"',,?>

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

DB "abc" DW 1 DD ?

Если вы задаете меньше значений, чем имеется элементов, Тур- бо Ассемблер закончит инициализацию, используя для остальных эле- ментов начальные значения из определения структуры или объедине- ния.

ASTRUC <"ABC"> ; то же, что ASTRUC <"ABC",,>


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

CUNION STRUC CTYPE DB ? UNION ; начало объединения

; если CTYPE = 0, использовать следующее. STRUC CT0PAR1 DW 1 CT0PAR2 DW 2 ENDS

; если CTYPE = 1, использовать следующее. STRUC CT1PAR1 DW 3 CT1PAR2 DW 4 ENDS

ENDS ; конец объединения ENDS

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

CUNION <0,<<2,>,?>>

Эта директива эквивалентна следующему:

DB 0 DW 2 DB 2 DB 2 DUP (?)

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

Когда вы определяете таблицу, нужно задать начальное значе- ние для всех ее элементов. Простейший инициализированный экземп- ляр таблицы содержит просто заданные в определении начальные данные, например:
TTYPE {}
что эквивалентно следующему:
DW MoveRtn DD MsgRtn DW DoneRtn
Фигурные скобки ({}) представляют нулевое инициализирующее значение. Значение инициализатора определяет, что элементы (если они есть) имеют начальное значение, которое нужно переопределить, и каким именно новым значением его требуется переопределить при выделении данных для экземпляра таблицы.
Инициализатор записи в фигурных скобках имеет следующий син- таксис:
{[имя_элемента = выражение [,имя_элемента = выражение.]]}
где "имя_элемента" - это имя элемента таблицы, а "выражение" - это значение, которое вы хотите определить для элемента в данном экземпляре. Пустое значение указывает, что вы будете использовать начальное значение элемента из определения таблицы. Значение ? эквивалентно нулю. Турбо Ассемблер устанавливает для всех элемен- тов, которые не указываются в инициализаторе, начальное значение, заданное в определении таблицы. Например:
TTYPE {MoveProc=MoveRtn2,DoneProc=?}
что эквивалентно следующему:
DW MoveRtn2 DD MsgRtn DW ?

Инициализация экземпляров записи

При определении записи для некоторых или всех полей записи нужно задавать начальное значение. (Турбо Ассемблер предполагает, что все не заданные значения равны 0.) Простейший инициализиро- ванный экземпляр записи содержит просто заданное в определении начальное поле данных, например:
MYREC {}
что эквивалентно следующему:
DW (4 SHL 6) + (0 SHL 4) + (15 SHL 0) ; SHL - это операция сдвига влево для выражений
Фигурные скобки ({}) представляют нулевое инициализирующее значение записи. Значение инициализатора определяет, что началь- ное значение нужно переопределить, и каким именно новым значением его требуется переопределить (если вы выделяете данные для эк- земпляра записи).
Инициализатор записи в фигурных скобках имеет следующий син- таксис:
{ [имя_поля = выражение [,имя_поля = выражение.]] }
где "имя_поля" - это имя поля записи, а "выражение" - это значе- ние, которое вы хотите определить для поля в данном экземпляре. Пустое значение указывает, что вы будете использовать начальное значение поля из определения записи. Значение ? эквивалентно ну- лю. Турбо Ассемблер устанавливает для всех полей, которые не ука- зываются в инициализаторе, начальное значение, заданное в опреде- лении записи. Например:
MYREC {VEL=2,SZE=?}
эквивалентно следующему:
DW (2 SHL 6) + (0 SHL 4) + (0 SHL 0)
Альтернативным методом инициализации экземпляров записи является использование угловых скобок (<>). В этом случае скобка- ми ограничивается инициализатор. Значения инициализатора являются неименованными, но располагаются в том же порядке, что и соот- ветствующие поля в определении записи. Данный инициализатор имеет следующий синтаксис:
<[выражение [,выражение.]]>
где "выражение" представляет нужное значение поля соответствующей записи в определении записи. Пустое значение указывает, что вы будете использовать начальное значение из определения записи. Ключевое слово ? указывает, что это поле должно быть равно 0. Например:
MYREC <,2,?>
эквивалентно следующему:
DW (4 SHL 6) + (2 SHL 4) + (0 SHL 0)
Если вы задаете меньше значений, чем имеется полей в записи, Турбо Ассемблер завершает экземпляр, используя для оставшихся по- лей начальные значения из определения записи.
MYREC <1> ; то же, что MYREC <1,,>

Инициализация таблицы виртуальных методов

Чтобы вы могли вызывать виртуальные методы, простой реализа- ции таблицы виртуальных методов недостаточно. Каждый объект с виртуальными методами включает в свои структуры данных указатель на таблицу виртуальных методов. Когда вы создаете экземпляр объ- екта этот указатель нужно инициализировать. Для этой цели можно использовать директиву TBLINST.
Инициализируйте указатель таблицы виртуальных методов в ме- тоде init следующим образом:
; Инициализация объекта связанного списка ; Это метод "init" ; Этот метод должен быть статическим. list_init PROC PASCAL FAR ARG @@list:dword USES dx,bx lds bx,@@list ; -- По адресу ds:bx инициализировать таблицу виртуаль- ; ных методов. TBLINIT ds:bx ; -- Инициализировать данные объекта ;; << Здесь инициализируются все данные объекта >> ret ENDP
Примечание: Заметим, что метод init должен быть стати- ческим, так как виртуальный метод экземпляра объекта вы вы- зывать не можете, пока не инициализирован указатель таблицы виртуальных методов.

Instruction can be compacted with override

(Возможно сокращение длины команды, если явно указать тип имени)
Из-за наличия ссылки вперед на имя идентификатора генерируе- мый код содержит дополнительные команды NOP. Для того чтобы сок- ратить код можно убрать ссылку вперед либо явно указать тип сим- волического имени. Например:
jmp X ; для этой команды будет выдано предупреждающее ; сообщение jmp SHORT X ; не будет выдано предупреждающего сообщения Х:

Инструкции PUSH и POP с несколькими операндами

На одной строке вы можете задать несколько инструкций PUSH и POP. Например:
PUSH ax PUSH bx PUSH cx POP cx POP bx POP ax
можно записать как:
PUSH ax bx cx POP cx bx ax
Чтобы Турбо Ассемблер распознавал наличие нескольких операн- дов в этих инструкциях, нужно чтобы любой операнд не мог рассмат- риваться как часть соседнего операнда, например инструкция:
PUSH foo [bx]
может привести к непредвиденному результату, поскольку foo,[bx] и foo[bx] являются допустимыми выражениями. Чтобы эта инструкция стала определенней, можно использовать круглые или квадратные скобки, например:
PUSH [foo] [bx]

Инструкция GETFIELD

Инструкция GETFIELD извлекает данные из поля записи. Логика ее работы противоположна инструкции SETFIELD. Она имеет следующий синтаксис:
GETFIELD имя_поля регистр_приемник, регистр/память_источник
где "имя_поля" и "регистр_приемник" работают так же, как в опера- ции SETFIELD. "Регистр/память_источник" можно использовать также, как "регистр_источник" в операции SETFIELD, например:
FOO RECORD R0:1,R1:4,R2:3,R3:1 . . . GETFIELD F1 AX,BL ; рабочим регистром является BX GETFIELD F1 AX,BH ; недопустимо!
Примечание: Операция GETFIELD уничтожает все содержи- мое рабочего регистра.
Операция GETFIELD извлекает значение поля, обнаруженное в регистре-источнике или по адресу памяти, и устанавливает в это значение соответствующую часть регистра-приемника. На другие ре- гистры (кроме рабочего) и флаги процессора эта инструкция не вли- яет.
Чтобы выполнить свою функцию, инструкция GETFIELD генерирует эффективную и расширенную последовательность инструкций MOV, XCHG, ROL и ROR.

Insufficient memory to process command line

(Не хватает памяти для обработки командной строки)
Командная строка занимает более 64К, либо для ее обработки не хватает имеющейся памяти. Следует упростить командную строку либо запустить Турбо Ассемблер при наличии большего объема памя- ти.

Internal error

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

Invalid command line

(Недопустимая командная строка)
Неверен формат командной строки, с помощью которой запуска- ется Турбо Ассемблер. Например, в команде:
NASM, MYFILE
не указан исходный файл, который должен ассемблироваться. См. Главу 2, где дается подробное описание командной строки.

Invalid model type

(Недопустимая модель памяти)
В директиве .MODEL для модели памяти указано недопустимое ключевое слово. Например:
.Model GIGANTIC
Для обозначения моделей памяти допустимы следующие ключевые слова: TINY, SMALL, COMPACT, MEDIUM, LARGE и HUGE.

Invalid number after _

(Недопустимый номер после _)
Верно указан идентификатор параметра, однако неверно специ- фицирован числовой параметр после нее. См. Главу 2, где описаны параметры командной строки.

Invalid operand(s) to instruction

(Недопустимый операнд(ы) для данной команды)
В команде указана недопустимая комбинация операндов. Напри- мер:
FADD ST(2), ST(3)
В команде FADD только на один стековый регистр можно ссы- латься по имени, другой операнд должен быть вершиной стека.

Использование для адресации к стеку регистра BP

Первый и наиболее часто используемый метод доступа к пара- метрам, передаваемым из Турбо Паскаля в Турбо Ассемблер, заключа- ется в том, чтобы использовать для адресации к стеку регистр BP. Например:
CODE SEGMENT ASSUME CS:CODE MyProc PROC FAR ; procedure MyProc(i,j : integer); PUBLIC MyProc j EQU WORD PTR [bp+6] ; j находится над сохраненным BP ; и адресом возврата i EQU WORD PTR [bp+8] ; i располагается над j push bp ; нужно сохранить BP вызывающей ; программы mov bp,sp ; BP теперь указывает на вершину ; стека mov ax,i ; адресуемся к i через BP . . .
При вычислении смешений в стеке параметров, к которым мы об- ращаемся таким образом, нужно помнить, что 2 байта используются для сохраненного регистра BP.
Обратите внимание на использование в данном примере присваи- ваний. Они позволяют сделать программу более понятной. У них есть только один недостаток: поскольку для выполнения такого рода при- сваиваний можно использовать только директиву EQU (а не =), в данной исходном файле Турбо Ассемблера вы не сможете переопреде- лить идентификаторы i и j. Один из способов обойти это заключает- ся в том, чтобы использовать более описательные имена параметров, чтобы они не повторялись, либо можно ассемблировать каждую подп- рограмму Ассемблера отдельно.

Использование другого базового или индексного регистра

Второй способ доступа к параметрам состоит в использовании для получения этих параметров другого базового или индексного ре- гистра (BX, SI или DI). Нужно однако помнить, что по умолчанию сегментным регистром для них является регистр DS, а не SS. Поэ- тому для их использования вам придется применять префикс переоп- ределения сегмента.
Приведем пример использования для получения параметров ре- гистра BX:
CODE SEGMENT ASSUME CS:CODE MyProc PROC FAR ; procedure MyProc(i,j : integer); PUBLIC MyProc j EQU WORD PTR SS:[BX+4] ; j находится над сохраненным ; BP и адресом возврата i EQU WORD PTR SS:[bp+8] ; i располагается над j mov bx,sp ; BX теперь указывает на вершину ; стека mov ax,i ; адресуемся к i через BX . . .
В тех программах, где нет большого числа ссылок на парамет- ры, такой метод позволяет сэкономить время и место. Почему? Пото- му, что в отличие от BP, регистр BX не требуется восстанавливать в конце программы.

Использование Extern "C" для упрощения компоновки

При желании вы можете использовать в ассемблерных функциях неисправленные имена, не пытаясь выяснить, как должны выглядеть правленные. Использование нескорректированных имен защитит ваши ассемблерные функции от возможных изменений алгоритма в будущем. Borland С++ позволяет определять в программах С++ стандартные имена функций С++, как в следующем примере:
extern "C" { int add(int *a, int b); }
Любые функции, объявленные внутри фигурных скобок, получат имена в стиле языка Си. Ниже показаны соответствующие определения в ассемблерном модуле:
public _add _add proc
Объявление ассемблерной функции в блоке extern "C" позволит вам избежать проблем со "откорректированными именами". При этом улучшится и читаемость кода.

Использование имен структур в выражениях

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

Использование корректировок сегментов

Турбо Паскаль генерирует файлы .EXE, которые могут загру- жаться в память компьютера РС по любому доступному адресу. Пос- кольку в программе заранее неизвестно, куда будет загружен данный сегмент программы, компоновщик указывает загрузчику DOS.EXE, что нужно при загрузке скорректировать в программе все ссылки на сег- менты. После выполнения этих корректировок все ссылки на сегменты (такие, как CODE или DATA) будут содержать корректные значения.
Ваша программа на Турбо Ассемблере может использовать это средство для получения адресов объектов во время выполнения. Предположим, например, что в вашей программе требуется изменить значение регистра DS, но вы не хотите сохранять в цикле исходное содержимое стека или перемещать эти значения во временную об- ласть. Вместо этого вы можете использовать операцию Турбо Ассем- блера SEG:
. . . mov ax,SEG DATA ; получить фактический ; адрес глобального значения ; DS Турбо Паскаля mov ds,ax ; поместить его в DS для ; использования Турбо ; Паскалем . . .
Когда ваша программа будет загружаться, DOS поместит коррек- тное значение SEG DATA прямо в поле промежуточного операнда инст- рукции MOV. Это наиболее быстрый путь перезагрузки сегментного регистра.
Данный метод нужно также использовать, чтобы программы обс- луживания прерываний сохраняли информацию в глобальном сегменте данных Турбо Паскаля. Регистр DS не обязательно во время прерыва- ния содержит значение DS Турбо Паскаля, но для получения доступа к переменным и типизованным константам Турбо Паскаля можно ис- пользовать указанную выше последовательность.

Использование в инструкциях PUSH и POP указателей

Стандартные инструкции PUSH и POP не могут сохранять в стеке дальние указатели, которые требуют 4 байт в процессорах 8086, 80186 и 80286 и 6 байт в процессоре 80386.
Турбо Ассемблер допускает указание в инструкциях PUSH и POP операнда-указателя размером DWORD для процессора 8086, 80186 и 80286 и QWORD для процессора 80386. Когда обнаруживаются такие инструкции, Турбо Ассемблер генерирует инструкции PUSH и POP для двух частей операнда.

Использование в макрокомандах символа &

Символ & в теле макрокоманды имеет специальное значение. В общем случае & отделяет имя формального аргумента от окружающего текста, благодаря чему Турбо Ассемблер может распознавать его при подстановке. Если, например, имеется следующее тело макрокоманды:
sym&foo: DB 'It is &foo time'
и если вы при вызове данной макрокоманды присвоите foo текстовую строку party, то фактическим включаемым в модуль текст будет сле- дующее:
symparty: DB 'It is party time'
В качестве другого примера можно привести следующий:
foo&sym: DB 'We are in O&foo&o'
Если вы при вызове данной макрокоманды присвоите foo тексто- вую строку hi, то фактическим включаемым в модуль текст будет следующее:
hisym: DB 'We are in Ohio'
Приведем правила для символа &:
- вне заключенных в кавычки строк символ & используется как разделитель;
- внутри заключенной в кавычки строки и после двоеточия, ко- торое не находится в заключенной в кавычки строке в режиме Ideal, символ & должен предшествовать формальному аргумен- ту (чтобы этот аргумент мог распознаваться);
- при расширении макрокоманды Турбо Ассемблер удаляет один символ & из группы таких символов.
Последний пункт дает возможность помещать внутри макрокоманд макроопределения, требующие символа &. Турбо Ассемблер будет уда- лять только один символ & из группы.

Использование включаемых файлов

Включаемые файлы позволяют вам использовать один и тот же блок кода в разных местах программы, включать этот блок в нес- кольких исходных модулях или уменьшить объем программы без необ- ходимости создания нескольких компонуемых модулей. Использование директивы INCLUDE указывает Турбо Ассемблеру, что нужно найти за- данный файл на диске и ассемблировать его, как если бы он был частью исходной программы.
Примечание: Допускается вложенность директив INCLUDE на любую глубину.
Синтаксис режима Ideal:
INCLUDE "имя_файла"
Синтаксис режима MASM:
INCLUDE имя_файла
где в имени файла может задаваться дисковод, каталог или расшире- ние. Если каталог или дисковод в имени файла не указываются, то Турбо Ассемблер сначала ищет файлы в каталогах, заданных в пара- метре командной строки /i, а затем в текущем каталоге.

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

Исправления
На стp. 333 в Приложении D, "Утилиты," говорится, что спра- вочное средство Турбо ассемблера называется TASMHELP. Вы можете использовать TASMHELP как описывается в данном руководстве только если установите Турбо ассемблер в каталоге, отличном от Borland C ++. В противном случае следует использовать утилиту THELP, кото- рая модифицирована таким образом, что может обращаться к множест- ву справочных файлов с помощью меню.
Утилиту THELP можно использовать следующим образом:
1) После установки Borland C++ и Турбо ассемблера вызовите THELP в ответ на подсказку DOS.
2) Чтобы вывести меню доступных справочных файлоа, нажмите клавиши ALT-F.
3) Для доступа к справочной информации по Турбо ассемблеру выберите файл TASM.TAH.
Назад | Содержание

Изменение используемого по умолчанию основания

Для изменения текущего используемого по умолчанию основания вы можете использовать директивы RADIX или .RADIX. В режиме Ideal используется следующий синтаксис:
RADIX выражение
а в режиме MASM:
.RADIX выражение
где "выражение" должно принимать значение 2 (двоичное), 8 (вось- меричное), 10 (десятичное) или 16 (шестнадцатиричное). Турбо Ас- семблер предполагает, что во время обработки директивы RADIX те- кущим основанием по умолчанию является основание 10.

Изменение размера стека

Начальный и завершающий код процедур работает с регистрами, указывающими на стек. В процессоре 80386 или 80486 сегменты могут быть 16- или 32-разрядными. Таким образом, перед генерацией кор- ректного начального и завершающего кода Турбо Ассемблеру нужно знать корректный размер стека.
Если вы выбираете стандартную модель с помощью директивы MODEL, размер стека выбирается автоматически.
В Турбо Ассемблере предусмотрены директивы, которые могут установить или переопределить используемый по умолчанию при гене- рации начального и завершающего кода размер стека. Список этих директив приведен в следующей таблице:
Директивы модификации размера стека Таблица 7.10 -----------------------T----------------------------------------¬ ¦ Директива ¦ Значение ¦ +----------------------+----------------------------------------+ ¦ SMALLSTACK ¦ Указывает, что стек 16-разрядный. ¦ ¦ ¦ ¦ ¦ LARGESTACK ¦ Указывает, что стек 32-разрядный. ¦ L----------------------+-----------------------------------------
Назад | Содержание | Вперед

Изменение упорядочивания сегментов в модуле

Порядок сегментов в каждом модуле определяет начальное раз- мещение сегментов в программе компоновщиком. В MASM 1.0 и 2.0 сегменты передавались компоновщику в алфавитном порядке. В Турбо Ассемблере предусмотрены директивы (только в режиме MASM), кото- рые позволяют воспроизвести это поведение.
Заметим, что данные директивы действуют так же, как парамет- ры командной строки /A и /S.

Эффективная генерация кода: директивы SMART и NOSMART

Эффективная генерация кода означает, что Турбо Ассемблер мо- жет определить, что можно использовать различные инструкции более эффективно, чем вы это делаете. Например, в некоторых случаях можно заменить инструкцию LEA более короткой и быстрой инструкци- ей MOV:
LEA AX,lval
можно заменить на:
MOV AX,OFFSET lval
Турбо Ассемблер предусматривает директивы, которые позволяют вам использовать эффективную генерацию кода. Эти директивы описа- ны в следующей таблице:
Директивы эффективной генерации кода Таблица 13.1 ------------------T----------------------------------------------¬ ¦ Директива ¦ Значение ¦ +-----------------+----------------------------------------------+ ¦ SMART ¦ Разрешает генерацию эффективного кода. ¦ ¦ ¦ ¦ ¦ NOSMART ¦ Запрещает генерацию эффективного кода. ¦ L-----------------+-----------------------------------------------
По умолчанию генерация эффективного кода разрешена. Однако на генерацию эффективного кода влияют не только директивы SMART и NOSMART, но и директива VERSION.
Примечание: О директиве VERSION подробнее рассказыва- ется в Главе 3.
Генерация эффективного кода влияет на следующие ситуации ге- нерации кода:
- Замена инструкции LEA инструкцией MOV, если операндом инс- трукции LEA является простой адрес.
- Где это возможно, выполняется генерация булевских инструк- ций со знаком,. Например, AND AX,+02 вместо AND AX,0002.
- Замена инструкции CALL FAR xxxx комбинацией инструкций PUSH CS, CALL NEAR xxxx, где целевой адрес xxxx использует тот же регистр CS.
Использование эффективных инструкций облегчает написание эф- фективного кода. Некоторые стандартные инструкции Intel также расширены таким образом, чтобы расширить их возможности и облег- чить использование. Они обсуждаются в следующих разделах.

Как обратиться за помощью к фирме Borland

Фирма Borland предлагает вам множество услуг, которые позво- лят ответить на вопросы о данном программном продукте. Убедитесь, что вы послали регистрационную карту. Для зарегистрированных пользователей обеспечивается техническое сопровождение, кроме то-
го, они могут получать информацию обо всех изменениях и дополне- ниях к продукту.

Ключевые слова директив

В следующем списке содержатся все ключевые слова директив Турбо Ассемблера. Ключевые слова группируются по версиям Турбо Ассемблера, в которых они вводились.
Следующие ключевые слова были введены в Турбо Ассемблере версии 1.0.
Ключевые слова Турбо Ассемблера 1.0 (VERSION T100) Таблица B.1 ----------------------------------------------------------------¬ ¦ % (1) %NOLIST JZ SETNLE ¦ ¦ .186 (M) NOLOCALS LAHF SETNO ¦ ¦ .286 (M) NOMASM51 LDS SETNP ¦ ¦ .286C (M) %NOMACS LEA SETNS ¦ ¦ .286P (M) NOMULTERRS LES SETNZ ¦ ¦ .287 (M) NOSMART LOCK SETO ¦ ¦ .386 (M) %NOSYMS LODS SETP ¦ ¦ .386C (M) %NOTRUNCS LODSB SETPE ¦ ¦ .386P (M) NOWARN LODSW SETPO ¦ ¦ .387 (M) ORG LOOP SETS ¦ ¦ .486 (M) %OUT (1) LOOPE SETZ ¦ ¦ .486C (M) P186 LOOPNE SHLD ¦ ¦ .486P (M) P286 LOOPNZ SHRD ¦ ¦ .8086 (M) P286N LOOPZ STOSD ¦ ¦ .8087 (M) P286P MOV LOOPW ¦ ¦ : (2) P287 MOVS LOOPWE ¦ ¦ = (2) P286 MOVSB LOOPWNE ¦ ¦ ALIGN P386N MOVSW LOOPWNZ ¦ ¦ .ALPHA P386P MUL LOOPWZ ¦ ¦ ARG P387 NEG F2XM1 ¦ ¦ ASSUME P486 NOP FABS ¦ ¦ %BI P486N NOT FADD ¦ ¦ .CODE (M) P8086 OR FADDP ¦ ¦ .CODESEG P8087 OUT FBLD ¦ ¦ COMM (10) PAGE POP FBSTP ¦ ¦ COMMENT (1) %PAGESIZE PUSH FCHS ¦ ¦ %CONDS %PCNT PUSHF FCLEX ¦ ¦ CONST PNO87 RCL FCOM ¦ ¦ .CONST (M) %POPLCTL RCR FCOMP ¦ ¦ .CREF (M) PROC (2) REP FCOMPP ¦ ¦ %CREF %PUSHLCTL REPE FCOS ¦ ¦ %CREFALL PUBLIC (1) REPNE FDECSTP ¦ ¦ %CREFREF PURGE REPNZ FDISI ¦ ¦ %CREFUREF QUIRKS REPZ FDIV ¦ ¦ %CTLS .RADIX (M) RET FDIVP ¦ ¦ .DATA (M) RADIX RETF FDIVR ¦ ¦ .DATA? (M) RECORD (2) RETN FDIVRP ¦ ¦ DATASEG REPT (1) ROL FENI ¦ ¦ DB .SALL (M) ROL FFREE ¦ ¦ DD SEGMENT (2) SAHF FIADD ¦ ¦ %DEPTH .SEQ (M) SAL FICOM ¦ ¦ DF (2) .SFCOND (M) SAR FICOMP ¦ ¦ DISPLAY SMART SBB FIDIV ¦ ¦ DOSSEG .STACK (M) SCAS FIDIVR ¦ ¦ DP (2) .STARTUP (M) SCASB FILD ¦ ¦ DQ (2) STRUC (2) SCASW FIMUL ¦ ¦ DT (2) SUBTTL (1) SHL FINCSTP ¦ ¦ DW (2) %SUBTTL [Р SHR FINIT ¦ ¦ ELSE (1) %SYMS STC FIST ¦ ¦ ELSEIF (1) %TABSIZE STD FISTP ¦ ¦ ELSEIF1 (1) %TEXT STI FISUB ¦ ¦ ELSEIF2 (1) .TFCOND (M) STOS FISUBR ¦ ¦ ELSEIFB (1) TITLE (1) STOSB FLD ¦ ¦ ELSEIFDEF (1) %TITLE STOSW FLDCTW ¦ ¦ ELSEIFDIF (1) %TRUNC SUB FLDENV ¦ ¦ ELSEIFDIFI (1) UDATASEG TEST FLDLG2 ¦ ¦ ELSEIFE (1) UFARDATA WAIT FLDLN2 ¦ ¦ ELSEIFIDN (1) UNION (2) WBINV FLDL2E ¦ ¦ ELSEIFIDNI (1) USES XCHG FLDL2T ¦ ¦ ELSEIFNB (1) WARN XLAT FLDPI ¦ ¦ ELSEIFNDEF (1) .XALL (M) XLATB FLDZ ¦ ¦ EMUL .XCREF (M) SOR FMUL ¦ ¦ END .XLIST (M) BOUND FMULP ¦ ¦ ENDIF (1) USECS ENTER FNCLEX ¦ ¦ ENDM USEDS INS FNDSI ¦ ¦ ENDP (2) USESS INSB FNENI ¦ ¦ ENDS (2) USEES INSW FNINIT ¦ ¦ EQU (2) USEFS LEAVE FNOP ¦ ¦ .ERR (1) (M) USEFS OUTS FNSAVE ¦ ¦ ERR CATSTR (2) OUTSB FNSTCW ¦ ¦ .ERR1 (1) (M) INSTR (2) OUTSW FNSTENV ¦ ¦ .ERR2 (1) (M) SIZESTR (2) POPA FNSTSW ¦ ¦ .ERRB (1) (M) SUBSTR (2) PUSHA FPATAN ¦ ¦ .ERRDEF (1) (M) AAA ARPL FPREM ¦ ¦ .ERRDIF (1) (M) AAD CTLS FPTAN ¦ ¦ .ERRDIFI (1) (M) AAM LAR FRNDINT ¦ ¦ .ERRE (1) (M) AAS LGDT FRSTOR ¦ ¦ .ERRIDN (1) (M) ADC LIDT FSAVE ¦ ¦ ERRIF ADD LLDT FSCALE ¦ ¦ .ERRDIF (1) (M) AND LMSW FSQRT ¦ ¦ .ERRDIFI (1) (M) ARPL LSL FST ¦ ¦ .ERRE (1) (M) BSF LTR FSTCW ¦ ¦ .ERRIDN (1) (M) BSR SGDT FSTENV ¦ ¦ ERRIF BSWAP SIDT FSTP ¦ ¦ ERRIF1 BT SLDT FSTSW ¦ ¦ ERRIF2 BTC SMSW FSTSW ¦ ¦ ERRIFB BT STR FSUB ¦ ¦ ERRIFDEF BTR VERR FSUBP ¦ ¦ ERRIFDIF BTS VERW FSUBR ¦ ¦ ERRIFDIFI CALL BSF FSUBRP ¦ ¦ ERRIFE CBW BSR FTST ¦ ¦ ERRIFDIDN CDQ BT FUCOM ¦ ¦ ERRIFDIDNI CLC BTC FUCOMP ¦ ¦ ERRIFNB CLD BTR FUCOMPP ¦ ¦ ERRIFNDEF CLI BTS FWAIT ¦ ¦ .ERRIFNB (1) (M) CLTS CDQ FXAM ¦ ¦ .ERRIFDEF (1) (M) CMC CMPSD FXCH ¦ ¦ .ERRNZ (1) (M) CMP CWDE FXTRACT ¦ ¦ .EVEN CMPS INSD FYL2X ¦ ¦ EVENDATA CMPS IRETD FYL2XP1 ¦ ¦ EXITM CMPXCHG IJECXZ FSETPM ¦ ¦ EXTRN (1) CWD LPS FPCOS ¦ ¦ .FARDATA CWDE LGS FPREM1 ¦ ¦ FARDATA DAA LODSD FPSIN ¦ ¦ .FARDATA (M) DAS LOOPD FPSINCOS¦ ¦ .FARDATA? (M) DEC LOOPDE FUCOM ¦ ¦ GLOBAL (1) DIV LOOPDNE FUCOMP ¦ ¦ GROUP (2) ESC LOOPDNZ FUCOMPP ¦ ¦ %INCL HLT LOOPDZ ¦ ¦ IDEAL IDIV LSS ¦ ¦ IF (1) IMUL MOVSD ¦ ¦ IF1 (1) IN MOVSX ¦ ¦ IF2 (1) INC MOVZX ¦ ¦ IFb (1) INS OUTSD ¦ ¦ IFDEF (1) INT POPAD ¦ ¦ IFDIFI (1) INVO POPFD ¦ ¦ IFE (1) IRET PUSHAD ¦ ¦ IFIDN (1) JA PUSHFD ¦ ¦ FIDNI (1) JAE SCASD ¦ ¦ IFNB (1) JB SETA ¦ ¦ IFNDEF (1) JBT SETAE ¦ ¦ %INCTL (1) JCXZ SETB ¦ ¦ INCLUD (1) JE SETBE ¦ ¦ INCLUDELIB (1) JG SETC ¦ ¦ IRP (1) JGT OUTSD ¦ ¦ IRPC (1) JL POPAD ¦ ¦ JUMPS JLE POPFD ¦ ¦ LABEL (2) JNA PUSHAD ¦ ¦ .LALL (M) JNAE PUSHFD ¦ ¦ .LFCOND (M) JNB SCASD ¦ ¦ %LINUM JNBE SETA ¦ ¦ %LIST JNC SETAE ¦ ¦ .LIST (M) JNE SETB ¦ ¦ LOCAL JNG SETBE ¦ ¦ LOCALS JNGE SETC ¦ ¦ MASM JNL SETE ¦ ¦ %MACS JNLE SETG ¦ ¦ MACRO (2) JNO SETGE ¦ ¦ MODEL JNP SETL ¦ ¦ .MODEL (M) JNC SETLE ¦ ¦ MULTERRS JNP SETNA ¦ ¦ NAME (1) JNS SETNAE ¦ ¦ %NEWPAGE JNZ SETNB ¦ ¦ %NOCONDS JO SETNBE ¦ ¦ %NNOCREF JP SETNC ¦ ¦ %NOCTLS JPE SETNE ¦ ¦ NOEMUL JPO SETNG ¦ ¦ %NOINCL JS SETNGE ¦ ¦ NOJUMPS JUMP SETNL ¦ L----------------------------------------------------------------

В Турбо Ассемблере версии 2. 0 поддерживаются все ключевые слова версии 1.0 со следующими дополнениями:

Новые ключевые слова Турбо Ассемблера версии 2.0 (VERSION T200) Таблица B.2 ----------------------------------------------------------------¬ ¦ BSWAP P486 STARTUPCODE ¦ ¦ CMPXCHG P486N WBINVD ¦ ¦ INVD PUBLICDLL(I) XADD ¦ ¦ INVLPG RETCODE ¦ L----------------------------------------------------------------

В Турбо Ассемблере версии 2.5 поддерживаются все ключевые слова версии 2.0 со следующими дополнениями:

Новые ключевые слова Турбо Ассемблера версии 2.5 (VERSION T250) Таблица B.3 ----------------------------------------------------------------¬ ¦ ENTERD LEAVED ¦ ¦ ENTERW LEAVEW ¦ L----------------------------------------------------------------

В Турбо Ассемблере версии 3.0 поддерживаются все ключевые слова версии 2.5 со следующими дополнениями:

Новые ключевые слова Турбо Ассемблера версии 3.0 (VERSION T200) Таблица B.4 ----------------------------------------------------------------¬ ¦ CLRFLAG LARGESTACK TBLINIT ¦ ¦ ENUM(2) SETFIELD TBLINST ¦ ¦ FASTIMUL SETFLAG TBLPTR ¦ ¦ FLIPFLAG SMALLSTACK TESTFLAG ¦ ¦ GETFIELD TABLE(2) VERSION ¦ ¦ GOTO(1) WHILE(1) ¦ L----------------------------------------------------------------

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

Ключевые слова и предопределенные идентификаторы

В данном разделе приведен полный перечень всех ключевых слов Турбо Ассемблера. Значение в скобках после ключевых слов указыва- ют их приоритет (1 или 2) в режиме MASM. Приоритет для ключевых слов указан только, если он имеет значение 1 или 2. Для всех дру- гих ключевых слов подразумевается приоритет 3. Турбо Ассемблер распознает ключевые слова только в том случае, если он находит их. В режиме MASM ключевые слова приоритета 1 или 3 всегда нахо- дятся в первой позиции, а ключевые слова приоритета 2 - во второй позиции. Символ M после ключевого слово указывает, что оно допус- тимо только для режима MASM, а I показывает ключевое слово, дос- тупное только в режиме Ideal. Если буква не указана, то ключевые слова работают в обоих режимах. Номер после ключевого слова пока- зывает приоритет.

Кое-что еще о вызове методов

Часто может встречаться ситуация, когда необходимо вызвать метод порождающего объекта из процедуры метода порожденного объ- екта. Для этого также можно использовать оператор CALL.METHOD.
Аналогично инструкции CALL.METHOD вы можете использовать расширение инструкции JMP с ключевым словом METHOD. Эта инструк- ция обеспечивает оптимальную рекурсию. См. Главу 13, где об инс- трукциях CALL.METHOD и JMP.METHOD рассказывается подробнее.

Командная строка компоновщика

Простейший способ скомпоновать модули Borland C++ с модулями Турбо Ассемблера состоит в том, чтобы ввести одну командную стро- ку Borland C++, после чего он выполнит всю остальную работу. При задании нужной командной строки Borland C++ выполнит компиляцию исходного кода Си, вызовет Турбо Ассемблер для ассемблирования, а затем вызовет утилиту TLINK для компоновки объектных файлов в вы- полняемый файл. Предположим, например, что у вас есть программа, состоящая из файлов на языке Си MAIN.CPP и STAT.CPP и файлов Ас- семблера SUMM.ASM и DISPLAY.ASM. Командная строка:
bcc main.cpp stat.cpp summ.asm display.asm
выполняет компиляцию файлов MAIN.CPP и STAT.CPP, ассемблирование файлов SUMM.ASM и DISPLAY.ASM и компоновку всех четырех объектных файлов, а также кода инициализации С++ и необходимых библиотечных функций в выполняемый файл MAIN.EXE. При вводе имен файлов Ас- семблера нужно только помнить о расширениях .ASM.
Если вы используете утилиту TLINK в автономном режиме, то генерируемые Турбо Ассемблером объектные файлы представляют собой стандартные объектные модули и обрабатываются также, как объек- тные модули С++. Описание TLINK в автономном режиме см. в Прило- жении С.

Комментарии в конце строки

Комментировать код Ассемблера можно несколькими способами. Один из методов состоит в добавлении комментария в конец строки, используя точку с запятой, например:
mov [bx],a1 ; записать измененный символ
Другой способ комментирования исходного кода заключается в использовании в качестве символа комментария символа продолжения строки (\). (См. ниже раздел "Продолжение строки").

Комментарии в программах

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

Компоновка ассемблерных модулей с С++

Важной концепцией С++ является безопасная с точки зрения стыковки типов компоновка. Компилятор и компоновщик должны рабо- тать согласованно, чтобы гарантировать правильность типов переда- ваемых между функциями аргументов. Процесс, называемый "корректи- ровкой имен" (name-mangling), обеспечивает необходимую информацию о типах аргументов. "Корректировка имени" модифицирует имя функ- ции таким образом, чтобы оно несло информацию о принимаемых функ- цией аргументах.
Когда программа пишется целиком на С++, корректировка имен происходит автоматически и прозрачно для программы. Однако, когда вы пишете ассемблерный модуль для последующей его компоновки с программой на С++, вы сами обязаны обеспечить корректировку имен в модуле. Это легко сделать, написав пустую функцию на С+ и ском- пилировав ее с ассемблерным модулем. Генерируемый при этом Borland С++ файл .ASM будет содержать исправленные имена. Затем вы можете их использовать при написании реального ассемблерного модуля.
Например, следующий фрагмент кода определяет четыре различ- ные версии функции с именем test:
void test() { }
void test( int ) { }
void test( int, int ) { }
void test( float, double ) { }
Если этот код компилируется с параметром -S, то компилятор создает на выходе файл на языке Ассемблера (.ASM). Вот как он выглядит (несущественные детали убраны):
; void test() @testSqv proc near push bp mov bp,sp popo bp ret @testSqv endp
; void test( int ) @testSqi proc near push bp mov bp,sp popo bp ret @testSqi endp
; void test( int, int ) @testSqii proc near push bp mov bp,sp popo bp ret @testSqii endp
; void test( float, double ) @testSqfd proc near push bp mov bp,sp popo bp ret @testSqfd endp

Компоновка программы

После ассемблирования файла HELLO.ASM вы продвинулись только на один шаг в процессе создания программы. Теперь, если вы ском- понуете только что полученный объектный код в выполняемый вид, вы сможете запустить программу.
Для компоновки программы используется программа TLINK, представляющая собой поставляемый вместе с Турбо Ассемблером ком- поновщик. Введите командную строку:
TLINK HELLO
Здесь опять не требуется вводить расширение имени файла. Компоновщик TLINK по умолчанию предполагает, что этим расширением является расширение .OBJ. Когда компоновка завершится (самое большее через несколько секунд), компоновщик автоматически прис- воит файлу с расширением .EXE имя, совпадающее с именем вашего объектного файла (если вы не определили другое имя). При успешной компоновке на экране появляется сообщение:
Turbo Linker Version 3.0 Copyright (c) 1988, 1991 by Borland International Inc.
В процессе компоновки могут возникнуть ошибки (в данной программе это маловероятно). Если вы получили сообщения об ошиб- ках компоновки (они выводятся на экран), измените исходный код программы так, чтобы он в точности соответствовал тексту програм- мы в приведенном выше примере, а затем снова выполните ассембли- рование и компоновку.

Компоновка с кодом инициализации С++

Хорошим правилом является вызов библиотечных функций Borland C++ только из Ассемблера в программах, которые компонуются с мо- дулем инициализации С++ (используя его в качестве первого компо- нуемого модуля). Этот "надежный" класс включает в себя все прог- раммы, которые компонуются с помощью командной строки TC.EXE или TCC.EXE, и программы, в качестве первого компонуемого файла кото- рых используется файл C0T, C0S, C0C, C0M, C0L или C0H.
В общем случае вам не следует вызывать библиотечные функции Borland C++ из программ, которые не компонуются с модулем инициа- лизации Borland C++, так как некоторые библиотечные функции Borland C++ не будут правильно работать, если не выполнялась ком- поновка с кодом инициализации. Если вы действительно хотите вызы- вать библиотечные функции Borland C++ из таких программ, мы пред- лагаем вам взглянуть на код инициализации (файл C0.ASM на дистри- бутивных дисках Borland C++) и приобрести у фирмы Borland исход- ный код библиотеки языка С++, после чего вы сможете обеспечить правильную инициализацию для нужных библиотечных функций.
Вызов определяемых пользователем функций С++, которые в свою очередь вызывают библиотечные функции языка С++, попадают в ту же категорию, что и непосредственный вызов библиотечных функ- ций С++. Отсутствие кода инициализации С++ может вызывать ошибки в любой программе Ассемблера, которая прямо или косвенно обраща- ется к библиотечным функциям С++.

Константы в выражениях

В качестве операндов в выражениях вы можете использовать константы, например:
mov ax,5 ; "5" - это операнд-константа

Константы

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

Косвенные командные файлы

В любой момент, когда вы вводите командную строку, Турбо Ас- семблер позволяет вам задавать косвенный командный файл, с по- мощью указания перед его именем символа @. Например:
TASM /DTESTMODE @MYPROJ.TA
Эта команда приводит к тому, что содержимое файла MYPROJ.TA становится частью командной строки (как если бы вы ввели ее со- держимое непосредственно).
Это полезное средство позволяет вам поместить наиболее часто используемые командные строки и списки файлов в отдельный файл. При этом нет необходимости помещать всю командную строку в один косвенный файл, поскольку в одной командной строке допускается использовать несколько исходных файлов с обычными аргументами, например:
TASM @MYFILES @IOLIBS /DBUF=1024
Таким образом вы можете использовать длинный список стандар- тных файлов и параметров, благодаря чему можно легко изменять по- ведение Ассемблера при каждом ассемблировании.
Вы можете либо поместить все имена и параметры файлов в одну строку командного файла, либо разбить их на несколько строк, как это необходимо.

Labels can't start with numeric characters

(Метки не могут начинаться с цифровых символов)
Обнаружено имя, которое не является ни допустимым числом, ни допустимым именем идентификатора. Например: 123XYZ.

Лексическая грамматика

допустимая_строка ::= пропуск допустимая_строка знак_пунктуации допустимая_строка числовая_строка допустимая_строка идентификатор_строки допустимая_строка нуль
пропуск ::= символ_пробела пропуск символ_пробела
символ_пробела ::= все управляющие символы, символы > 128, ' '
идентификатор_строки ::= идентификатор_символа идентификатор_строки_2
идентификатор_строки_2 ::= идентификатор_символа_2 идентификатор_строки_2 нуль
идентификатор_символа ::= $,%,_,?,символы алфавита
идентификатор_символа_2 ::= идентификатор_символов плюс цифры
числовая_строка ::= числ_строка стр_строка
числ_строка ::= цифры буквенно_цифровые_символы цифры'.'цифры показатель_степени цифры показатель_степени ; Только в режиме MASM в директи- ; вах DD, DQ и DT
цифры ::= цифра цифры цифра
цифра ::= от 0 до 9
алфавитно-цифровые_символы ::= цифра буквенно_цифровые_символы буква буквенно_цифровые_символы нуль
буква ::= буквы алфавита
показатель_степени ::= Е+цифры Е-цифры Ецифры нуль
стр_строка ::= строка в двойных кавычках; кавычка, вводимая двумя кавычками
знак_пунктуации ::= любой символ, отличный от следующих: символ_пробела,идентификатор_символ,'"',"'" или цифры
Символ точки (.) трактуется различным образом в режиме MASM и в режиме Ideal. Этот символ не требуется указывать в числах с плавающей запятой в режиме MASM. Точка не может входить в состав символического имени в режиме Ideal. В режиме MASM с этого симво- ла иногда начинаются символические имена, кроме того, знак пунк- туации используется иногда в качестве селектора поля структуры.
Правила интерпретации точки (.):
1. В режиме Ideal точка всегда интерпретируется как знак пунктуации.
2. В режиме MASM точка трактуется как первый символ иденти- фикатора в следующих случаях:
а). Когда она является первым символом в строке и в ряде некоторых других специальных случаев, например, в составе внешних (EXTRN) и общих (PUBLIC) символичес- ких имен, она присоединяется к следующему за ней сим- волическому имени, если следующий за ней символ представляет собой идентификатор_символа_2, как он определен выше.
б). Если она не является первым символом в строке, или если результирующее имя идентификатора будет предс- тавлять собой определенное имя идентификатора, тогда точка присоединяется к началу следующего за ней иден- тификатора.

Line too long - truncated

(Строка слишком длинная - производится усечение)
Текущая строка исходного файла содержит более 255 символов. Лишние символы игнорируются.

Литеральные строки в угловых скобках

Используйте угловые скобки для выделения литеральной строки, содержащей символы-разделители. Следует использовать следующий синтаксис:
<текст>
где "текст" интерпретируется как одиночный строковый параметр, даже если он содержит запятые, пробелы или символы табуляции, ко- торые обычно разделяют параметры. Используйте данную операцию, когда вы хотите передать аргумент, содержащий один из таких сим- волов.
Вы можете также использовать эту операцию, чтобы Турбо Ас- семблер интерпретировал символ литерально, не придавая ему специ- альное значение. Например, если вы хотите передать в качестве па- раметра макровызова точку с запятой (;), то чтобы предотвратить ее интерпретацию как комментария, вы можете заключить ее в угло- вые скобки (<;>). При преобразовании заключенной в скобки строки в текстовый аргумент Турбо Ассемблер убирает только один уровень угловых скобок. Это позволяет вызывать макрокоманду, требующую наличия угловых скобок, из другой макрокоманды.

Литература по языку Ассемблера

Многие ведущие издательства издают по продуктам фирмы Borland превосходные книги, предназначенные для любого круга чи- тателей - от начинающих пользователей до тех, кто имеет достаточ- ный опыт работы. Приведем несколько названий книг, в которых можно найти дополнительную информацию о Турбо Ассемблере, Турбо отладчике и Турбо Профайлере:
Ackerman, Charles. Turbo Debugger and Tools: A Self-Teaching Guide, John Wiley and Sons (New York: 1990).
Swan, Tom. Mastering Turbo Assembler. Indianapolis: Hayden Books, 1989.
Swan, Tom. Mastering Turbo Debugger and Tools. Howard W. Sams and Co. (Carnel, IN: 1990).
Syck, Gary. The Waite Group's Turbo Assembler Bible, Howard W. Sams and Co. (Carmel, IN: 1990).
Назад | Содержание | Вперед

Location counter overflow

(Переполнение счетчика адреса)
Текущий сегмент заполнен, последующий код или данные затрут начало сегмента. Например:
ORG 0FFF0h ARRAY DW 20 DUP (0) ; переполнение

Логические арифметические операции

Логические арифметические операции позволяют вам выполнять операции булевской алгебры. Каждая из этих операций выполняется поразрядно, то есть, логическая операция выполняется по одному биту. Логические операции приведены в следующей таблице.
Логические арифметические операции Таблица 5.16 ------------------------------T---------------------------------¬ ¦ Выражение ¦ Значение ¦ +-----------------------------+---------------------------------+ ¦ NOT выражение ¦ Поразрядное дополнение выраже- ¦ ¦ ¦ ния. ¦ ¦ ¦ ¦ ¦ выражение_1 AND выражение_2 ¦ Поразрядная операция "И". ¦ ¦ ¦ ¦ ¦ выражение_1 OR выражение_2 ¦ Поразрядная операция "ИЛИ". ¦ ¦ ¦ ¦ ¦ выражение_1 XOR выражение_2 ¦ Поразрядная операция "исключа- ¦ ¦ ¦ ющее ИЛИ". ¦ L-----------------------------+----------------------------------

Локальные формальные аргументы

В начале тела макрокоманды вы всегда можете включить одну или более директив LOCAL. Директива LOCAL описывает специальные формальные аргументы, которым при каждом расширении макрокоманды будет присваиваться уникальное символьное имя.
Синтаксис директивы LOCAL в теле макрокоманды выглядит сле- дующим образом:
LOCAL формальный_аргумент_1 [,формальный_аргумент_2].
Если имя "формальный_аргумент", указанное в директиве LOCAL, не содержит префикс локального идентификатора, то присваиваемое ему уникальное имя идентификатора будет иметь вид ??xxxx, где xxxx представляет шестнадцатиричное число. В противном случае уникальное символьное имя будет иметь вид <локальный_пре- фикс>xxxx.
Примечание: Подробности о разрешении локальных иденти- фикаторов и задании локального префикса можно узнать в Гла- ве 11.
Вы можете использовать формальные аргументы LOCAL для зада- ния в теле макрокоманды меток, например:
LOCAL @agn, @zero XOR dx,dx MOV cx,exp MOV ax,1 JCXZ @zero MOV bx,factor @agn: MUL bx LOOP @agn @zero:

Локальные метки в стиле MASM

В MASM 5.1 и 5.2 предусмотрены специальные идентификаторы, которые вы можете использовать для управления областью действия ближних меток в небольшом диапазоне строк. Это идентификаторы @@, @F и @B.
Когда вы описываете @@, как ближнюю метку, используя двоето- чие (:), то определяете уникальный идентификатор вида @@xxxx (где xxxx - это шестнадцатиричное число). @B ссылается на последний определенный таким образом идентификатор. @F ссылается на следую- щий идентификатор с определением такого вида. Например:
version m510 @@: jmp @B ; перейти на предыдущий идентификатор @@ jmp @F ; перейти на следующий идентификатор @@ @@: jmp @B ; перейти на предыдущий идентификатор @@ jmp @F ; ошибка: нет следующего @@
Назад | Содержание | Вперед

Макрокоманды повторения строк

Для повторения тела макрокоманды для каждого элемента в списке или каждого символа в строке вы можете использовать макро- директивы повторения IRP и IRPC. Каждая из этих директив требует от вас задания одного формального аргумента. Приведем синтаксис директивы IRP:
IRP формальный_аргумент, список_аргументов тело_макрокоманды ENDM
Директива IRPC имеет следующий синтаксис:
IRP формальный_аргумент, строка тело_макрокоманды ENDM
в обоих случаях "формальный_аргумент" - это аргумент, используе- мый в теле макрокоманды. Конец тела макрокоманды отмечает ENDM.
В синтаксисе директивы IRP "список_аргументов" состоит из спискам аргументов, разделенных запятыми. Аргументами может быть любой текст (идентификаторы, строки, числа и т.д.). Форма каждого аргумента в списке аналогична той, которая описана выше для вызо- ва общей макрокоманды, состоящей из нескольких строк. Список ар- гументов всегда нужно заключать в угловые скобки (<>).
В синтаксисе директива IRPC аргумент состоит из одной стро- ки. Строка может содержать столько символов, сколько вы хотите.
Для каждого аргумента или символа в строке Турбо Ассемблер будет включать в модуль тело макрокоманды, подставляя вместо фор- мального аргумента аргумент или символ (когда он его находит). Например:
IRP reg PUSH reg ENDM
дает следующее:
PUSH ax PUSH bx PUSH cx PUSH dx
а директива IRPC:
IRPC LUCKY,1379 DB LUCKY ENDM
дает следующее:
DB 1 DB 2 DB 3 DB 4
Используйте директива IRPC аккуратно, поскольку Турбо Ас- семблер помещает каждый символ в строке в макрорасширение "как есть", поэтому макрокоманда строкового повторения:
IRPC CHAR,HELLO DB CHAR ENDM
может дать не DB 'H','E'','L','L','O', а DB H,E,L,L,O (где каждая буква интерпретируется как имя идентификатора.

Макрокоманды, состоящие из нескольких строк

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

Method call requires object name

(В вызове метода необходимо имя объекта)
Оператор CALL.METHOD не может получить тип объекта из эк- земпляра указателя. Вы должны указать имя объекта.

Missing argument list

(Отсутствует список аргументов)
В директиве IRP или IRPC, определяющей блок повторения, не указан список аргументов для формального параметра. Например:
IRP X ; нет списка аргументов DB X ENDM
В директивах IRP и IRPC обязательно должен быть указан фор- мальный параметр и список аргументов.

Missing argument or <

(Отсутствует аргумент, либо не указана угловая скобка <)
Не указаны угловые скобки, либо вообще отсутствует выражение в угловых скобках там, где оно необходимо. Например:
if b ; должно быть указано выражение в угловых ; скобках

Missing argument size variable

(Отсутствует переменная для размера блока параметров)
В директиве ARG или LOCAL не указано имя идентификатора пос- ле знака равенства в конце оператора. Например:
ARG A:WORD,B:DWORD= ; ошибка, нет имени после знака= LOCAL X:TBYTE= ; та же ошибка
В директивах ARG и LOCAL после знака равенства, если он ука- зан, обязательно должно быть указано имя переменной для размера блока параметров.

Missing COMM ID

(Отсутствует идентификатор в директиве COMM)
В директиве COMM не указано имя идентификатора перед специ- фикатором типа. Например:
COMM NEAR ; ошибка: отсутствует ; имя идентификатора перед "NEAR"
В директиве СОММ обязательно должно быть указано имя иденти- фикатора. Имя идентификатора и спецификатор типа должны быть раз- делены двоеточием (:).

Missing dummy argument

(Отсутствует формальный параметр)
В директиве IRP или IRPC, определяющей блок повтора, отсутс- твует формальный параметр. Например:
IRP DB X ; нет формального параметра ENDM
В директивах IRP и IRPC обязательно должны быть указаны фор- мальный параметр и список аргументов.

Missing end quote

(Отсутствует закрывающая кавычка)
В конце строковой константы нет закрывающей кавычки. Напри- мер:
DB "abc ; отсутствует кавычка в конце ABC mov al,'X ; отсутствует кавычка после Х
Строковая константа должна оканчиваться той же кавычкой, ко- торой она начинается.

Missing macro ID

(Отсутствует идентификатор макрокоманды)
В директиве MACRO, определяющей макрокоманду, отсутствует имя. Например:
MACRO ; ошибка: не указано имя макрокоманды DB A ENDM
Имя в определении макрокоманды указывать обязательно.

Missing module name

(Отсутствует имя модуля)
В директиве NAME не указано имя модуля. Напомним, что дирек- тива NAME действует только в режиме Ideal.

Missing or illegal language ID

(Отсутствует или неверно указан идентификатор языка)
В директиве .MODEL неверно указан идентификатор языка. Под- робное описание директивы MODEL см. в Главе 7 настоящего руко- водства.

Missing or illegal type specifier

(Отсутствует или неверно указан спецификатор типа)
В операторе отсутствует или неверно указан обязательный па- раметр - идентификатор типа (BYTE, WORD и т.д.) Например:
RED LABEL XXX ; ошибка: "ХХХ" не является ; идентификатором типа

Missing table member ID

(Пропущен идентификатор элемента таблицы)
В операторе CALL.METHOD после ключевого слова METHOD пропущено имя объекта.

Missing term in list

(Отсутствует член в списке параметров)
В режиме Ideal в директиве, допускающей указание нескольких параметров (такой как EXTRN, PUBLIC и т.д.), отсутствует параметр после одной из запятых, отделяющих параметры друг от друга. Нап- ример:
EXTRN XXX:BYTE,,YYY:WORD
В режиме Ideal параметры в любом списке всегда разделяются только одной запятой, в конце списка запятой быть не должно.

Missing text macro

(Отсутствует текстовая макрокоманда)
В директиве не указан обязательный параметр - текстовая мак- рокоманда. Например:
NEWSTR SUBSTR ; для SUBSTR должны быть ; указаны параметры

Множества

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

Model must be specified first

(Сначала должна быть указана модель памяти)
Использована одна из упрощенных сегментных директив без предварительной спецификации модели памяти. Например:
.CODE ; ошибка: сначала нужно указать директиву ; .MODEL
Перед использованием упрощенных сегментных директив должна быть указана с помощью директивы .MODEL модель памяти.

Модели памяти и сегменты

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

Модификация вашей первой программы на Турбо Ассемблере

Теперь снова войдем в редактор и модифицируем программу та- ким образом, чтобы она могла воспринимать какие-то данные из внешней среды (этой "внешней средой" будете вы, а вводимые данные будут набираться на клавиатуре). Измените программу следующим об- разом:
.MODEL SMALL .STACK 100h .DATA TimePrompt DB 'Это время после полудня? (ДА/НЕТ) - [Y/N]$' GoodMorningMessage LABEL BYTE DB 13,10,'Доброе утро!',13,10,'$' GoodAfternoonMessage LABEL BYTE DB 13,10,'Здравствуйте!',13.10,'$' .CODE mov ax,@Data mov ds,ax ; установить регистр DS таким ; образом, чтобы он указывал ; на сегмент данных mov dx,OFFSET TimePrompt ; ссылка на сообщение-запрос mov ah,9 ; функция DOS вывода строки int 21h ; получить ответ из одного ; символа cmp al,'Y' ; указано время после полудня ; (прописная буква Y) jz IsAfternoon ; да, время указано после ; полудня cmp al,'y' ; указано время после полудня ; (строчная буква y) jnz IsMorning ; нет, время указано до ; полудня IsAfternoon: mov dx,OFFSET GoodAfternoonMessage ; указывает на ; приветствие "Здравствуйте" jmp DisplayGreeting IsMorning: mov dx,OFFSET GoodMorningMessage ; указывает на ; приветствие "Доброе утро" DisplayGreeting: mov ah,9 ; функция DOS вывода сообщения int 21h ; вывести соответствующее ; сообщение mov ah,4ch ; функция DOS завершения ; программы int 21h ; завершить программу END
Таким образом вы добавили в программу два очень важных новых средства: возможность ввода и принятие решений. Эта программа запрашивает у вас, является ли вводимое время временем после по- лудня, воспринимая ответ (один символ) с клавиатуры. Если таким ответом будет буква Y в верхнем или нижнем регистре (что означает ответ ДА), то программа выводит сообщение "Здравствуйте!", в про- тивном случае выводится сообщение "Доброе утро!". В данной прог- рамме имеются все основные элементы полезной программы: ввод из информации внешней среды, обработка данных и принятие решения.
Сохраните эту модифицированную программу на диске. После этого заново ассемблируйте и скомпонуйте программу, как в преды- дущем примере. Запустите ее снова, введя hello в ответ на подс- казку DOS. Выведется сообщение:

Это время после полудня? (ДА/НЕТ) - [Y/N]

Курсор будет мерцать у последнего символа в ожидании ввода ответа. Нажмите Y. Программа ответит:

Здравствуйте!

Таким образом HELLO.ASM стала теперь интерактивной програм- мой с принятием решений.

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

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

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

Чтобы получить распечатку программы (вывести ее на устройс- тво печати), обратитесь к руководству по редактору текстов. Ис- ходные файлы Турбо Ассемблера представляют собой обычные тексто- вые файлы в коде ASCII (американский стандартный код обмена информацией), поэтому вы можете также напечатать исходный текст программы на Ассемблере с помощью команды PRINT, введя ее в ответ на подсказку операционной системы DOS.

Module is pass-dependant - compatibility pass was done

(Модуль зависит от прохода. Выполнен проход, обеспечивающий совместимость с MASM)
Это предупреждающее сообщение выдается, если обнаружена конструкция, зависящая от прохода, и в командной строке указан параметр /m. В этом случае выполняется проход, обеспечивающий совместимость с MASM.

Name must come first

(Имя должно быть указано первым)
Имя идентификатора указано после названия директивы, тогда когда оно должно находиться перед названием директивы. Например:
STRUC ABC ; ошибка: "ABC" должно стоять перед ; ключевым словом STRUC
В режиме Ideal имя идентификатора указывается после названия директивы, поэтому эта ошибка часто возникает при попытках ас- семблирования в режиме MASM программ, предназначенных для работы в режиме Ideal.

Написание на языке Ассемблера функций-элементов С++

Хотя можно написать функцию-элемент класса С++ целиком на языке Ассемблера, это далеко не просто. Например, все функ- ции-элементы классов С++ имеют "откорректированные" имена, что обеспечивает безопасную по согласованности типов компоновку функ- ций и делает возможным переопределение функций, а ваша ассемблер- ная функция должна знать в точности, какое имя С++ ожидает для данной функции-элемента. Для доступа к переменным-элементам вы должны подготовить в ассемблерном коде определение STRUC, опреде- ляющее все переменные-элементы с точно совпадающими размерами и расположением. Если ваш класс является производным, то могут су- ществовать и другие переменные-элементы, производные от базового класса. Даже если класс не является производным (порожденным), то расположение переменных-элементов в памяти изменяется в случае, если класс этот включает в себя какие-либо виртуальные функции.
Если вы пишете функцию на встроенном Ассемблере, Borland С++ может взять на себя эти вопросы. Однако если вы работаете на язы- ке Ассемблера отдельно (например, переделываете уже имеющийся код), то существуют некоторые методы, позволяющие упростить эту работу.
Создайте определение фиктивной функции С++ для ассемблерной функции. Это определение удовлетворит компоновщик, так как будет содержать откорректированное имя функции-элемента. Эта фиктивная функция будет вызывать ассемблерную функцию и передавать ей пере- менные-элементы и прочие параметры. Так как ассемблерный код бу- дет иметь все нужные ему параметры посредством аргументов, вы мо- жете не заботиться об изменениях в определении класса. Ваша ассемблерная функция может быть описана в коде С++ как extern "C", что показано в примерах. Например (countadd.cpp):
class count_add { // Частные переменные-элементы (private) int access_count; // число обращений int count; // текущий счетчик public: count_add(void) { access_count=0; count=0; } int get_count (void) {return Count;}
// Две функции, которые будут фактически написаны на // Ассемблере:

void increment(void); void add(int what_to_add=-1); // Отметим, что умолчание влияет только // на вызовы add; оно не влияет на код add }

extern "C" { // Для создания уникальных и осмысленных имен // ассемблерных подпрограмм прибавим имя класса к // имени ассемблерной подпрограммы. В отличие от прочих // ассемблеров, Турбо Ассемблер не имеет проблем с // длиной имен. void count_add_increment(int *count); // Мы передадим // указатель на // переменную count. // Ассемблер выполнит // увеличение. void count_add_add(int *count,int what_to_add); }

void count_add::increment(void) { count_add_increment(&count); }

void count_add(int what_to_add) { count_add(&count, int what_to_add); }

Ваш ассемблерный модуль, определяющий подпрограммы count_add _increment и count_add_add, должен иметь вид (COUNTADD.ASM):

.MODEL small ; выбор модели small (ближние код и данные) .CODE PUBLIC _count_add_increment _count_add_increment PROC ARG count_offset:word ; Адрес переменной-элемента push bp ; Сохранение записи активации ; вызывающей программы mov bp,sp ; Установка собственной записи ; активации mov bx,[count_offset] ; Загрузка указателя inc word ptr [bx] ; Увеличение переменной-элемента pop bp ; Восстановление записи активации ; вызывающей программы _count_add_increment ENDP

PUBLIC _count_add_add _count_add_add PROC ARG count_offset:word,what_to_add:word push bp mov bp,sp mov bx,[count_offset] ; Загрузка указателя mov ax,[what_to_add] add [bx],ax pop bp ret _count_add_add ENDP

end

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

Near jump or call to different CS

(Адресат ближнего перехода или вызова находится в другом ко- довом сегменте)
Это сообщение об ошибке выдается при попытке осуществить ближний вызов или переход по адресу, определенному в области, где регистр CS указывает на другой сегмент.

Need address or register

(Требуется указать адрес или регистр)
Не указан второй операнд команды, т.е. операнд указываемый после запятой. Например:
mov ax, ; нет второго операнда

Need angle brackets for structure fill

(Значения для структуры должны указываться в угловых скоб- ках)
В операторе выделения памяти под структуру не указан список начальных значений. Например:
STR1 STRUC M1 DW ? M2 DD ? ENDS STR1 ; нет списка начальных значений

Need colon

(Требуется двоеточие)
В директиве EXTRN, GLOBAL, ARG и LOCAL отсутствует двоеточие перед спецификатором типа (BYTE, WORD и т.д.) Например:
EXTRN X BYTE,Y:WORD ; после Х нет двоеточия

Need expression

(Требуется указать выражение)
Выражение содержит операцию, для которой не указан операнд. Например:
Х = 4 + * 6

Need file name after INCLUDE

(В директиве INCLUDE должно быть указано имя файла)
В директиве INCLUDE не указано имя файла. Например:
INCLUDE ; не указано, какой файл должен быть включен
В режиме Ideal имя файла должно быть заключено в кавычки.

Need left parenthesis

(Отсутствует левая круглая скобка)
Опущена левая круглая скобка там, где это не допускается синтаксисом выражения. Например:
DB 4 DUP 7
Выражение в операции DUP обязательно должно быть заключено в круглые скобки.

Need method name

(Требуется имя метода)
Оператор CALL.METHOD требует после ключевого слова METHOD имени метода.

Need pointer expression

(Требуется выражение-указатель)
Эта ошибка возникает только в режиме Ideal и указывает, что выражение в квадратных скобках ([]) не является указателем на па- мять. Например:
mov ax,[word ptr]
В режиме Ideal в квадратных скобках допускается указывать только адресные выражения.

Need quoted string

(Требуется указать строку в кавычках)
Ошибка при вводе параметра директивы, который должен предс- тавлять собой строку, заключенную в кавычки. В режиме Ideal для ряда директив в качестве параметра указывается строка в кавычках. Например:
Ideal DISPLAY "Все сделано"

Need register in expression

(В выражении требуется указать имя регистра)
В выражении не указано имя регистра там, где это является обязательным.

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
LANGUAGE EQU
% 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 версий 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 ; недопустимо!

Область действия в границах блока

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

Общая структура модуля

В Турбо Ассемблере имеется несколько директив, которые помо- гают вам работать с модулями кода. Эти директивы описываются в оставшейся части данной главы.

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

Программы Турбо Ассемблера могут вызывать функции С++ и ссы- латься на внешние переменные Си. Программы 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> ;ошибка: можно указать либо <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.

Операции сравнения

Операции сравнения позволяют сравнить два выражение и прове- рить их равенство или неравенство или что одно из них больше или меньше другого. Эти операции равны -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.

Операции

Изменения, внесенные в операции выражений в режиме Ideal, позволяют повысить мощность и гибкость некоторых операций, оста- вив без изменения общее поведение выражений. Для того, чтобы со- действовать некоторым комбинациям операций, изменен порядок стар- шинства некоторых операций.
Аккуратно задавайте точку (.) в элементах структуры, на ко- торые вы ссылаетесь. Операция точки для элементов структур в ре- жиме Ideal является более строгой. Выражение слева от точки долж- но представлять собой имя элемента структуры. Приведем примеры загрузки регистров значениями конкретных элементов структур:
; Опишем переменные с помощью структурных типов S_Stuff SomeStuff <> O_Stuff OtherStuff <> mov ax,[S_Stuff.Amount] ; загрузить значение размером в ; слово mov bl,[O_Stuff.Amount] ; загрузить значение размером в ; байт

Операция :

Операция : определяет ближнюю метку кода и имеет синтаксис:
имя:
где "имя" - это идентификатор, который вы не объявляли ранее в исходном коде. Ближние метки кода вы можете размещать на строке кода, где содержится только одна метка, или в начале строки перед инструкцией. Обычно ближние метки кода используются в качестве адреса перехода в инструкциях 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

Директива LABEL позволяет вам создавать элементы структур без выделения данных. Обычно LABEL задает метку с именем или мар- кер в той точке, где она обнаруживается в сегменте. Директива LABEL внутри определения структуры определяет элементы этой структуры. Приведем синтаксис директивы LABEL:
LABEL имя сложный_тип
В режиме MASM вы можете использовать следующий синтаксис:
имя LABEL сложный_тип
где "имя" - это имя элемента структуры. "Тип" - это желаемый тип элемента структуры. Он может представлять собой любое допустимое имя типа. Описание имеющихся спецификаторов типов можно найти в Главе 5.

Определение меток

Метки позволяют вам присваивать значения идентификаторам. Существует три способа определения меток:
- использование операции :; - использование директивы LABEL; - использование операции :: (MASM 5.1).

Определение объекта

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

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

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

Определение перечислимых типов данных

Перечислимые типы данных представляют набор значений, кото- рые можно сохранять в определенном числе бит. Максимальное запи- санное значение определяет фактически необходимое число бит.
Приведем пример синтаксиса режима Ideal определения перечис- лимого типа данных:
ENUM имя [переменная_перечисления [, переменная_перечисления.]]
В режиме MASM вы можете использовать следующий синтаксис:
имя ENUM [переменная_перечисления [, переменная_перечисления.]]
Каждая "переменная_перечисления" имеет следующий синтаксис:
имя_переменной [=значение]
Когда вы присваиваете переменной "имя_переменной" конкретное значение, Турбо Ассемблер, если поле "значение" отсутствует, бу- дет присваивает ей значение, равное значению последней перемен- ной в списке, плюс единица. Значения не могут быть относительными или иметь опережающие ссылки. Переменные, создаваемые перечисли- мым типом (ENUM), представляют собой переопределяемые переменные с глобальной областью действия.
Предупреждение: Если вы используете в двух перечисли- мых типах данных одно и то же имя переменной, то первое значение переменной будет потеряно, и в результате возник- нет ошибка.
"Имя" - это имя типа данных ENUM. Для получения различной информации о присваивании значений переменным вы можете затем ис- пользовать это имя. О присваивании имен перечислимым типам данных в Турбо Ассемблере рассказывается в Главе 5.
Имена перечислимых типов данных можно также использовать для создания переменных и выделения памяти. Подробности см. в Главе 12.
Перечислимые типы данных являются переопределяемыми. В моду- ле вы можете несколько раз определить одно и то же имя перечисли- мого типа данных.
Кроме того, Турбо Ассемблер для определения перечислимого типа данных с большим числом переменных использует синтаксис, в котором указывается несколько строк. Начало такого определения отмечается символом {, а конец - символом }.
В режиме Ideal этот синтаксис имеет следующий вид:
ENUM имя [переменная_перечисления [, переменная_перечисления.]] {переменная_перечисления [,переменная_перечисления].] . . . {переменная_перечисления [,переменная_перечисления].] }
В режиме MASM используется следующий синтаксис:
имя ENUM [переменная_перечисления [, переменная_перечисления.]] {переменная_перечисления [,переменная_перечисления].] . . . {переменная_перечисления [,переменная_перечисления].] }
Например, все приведенные ниже перечислимые типы данных эк- вивалентны:
foo ENUM f1, f2, f3, f4 ; исходная версия
foo ENUM { ; версия из нескольких строк f1, f2, f3, f4 }
foo ENUM f1, f2, { f3, f4 } ; более компактная версия
Примечание: Турбо Ассемблер не распознает в определе- нии перечислимого типа данных из нескольких строк никаких псевдоопераций.

Определение структур и объединений

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

Определение таблиц

Табличный тип данных представляет набор элементов таблицы. Каждый элемент имеет заданный размер (в байтах) и начальное зна- чение. Элемент таблицы может быть виртуальным или статическим. Виртуальному элементу таблицы присваивается смещение в табличном типе данных. Для него резервируется место в каждом экземпляре таблицы. Статических элемент не имеет смещения. В экземпляре таб- лицы пространство для него не резервируется.
В целом размер табличного типа данных равен сумме размеров всех виртуальных элементов.
Табличные типы данных представляют таблицы методов, исполь- зуемые в объектно-ориентированном программировании. С объектом связано обычно некоторое число методов, которые являются указате- лями на процедуры, работающие с экземплярами объектов. Процедуры методов могут либо вызываться непосредственно (статические мето- ды), либо косвенно, через таблицу указателей процедур методов (виртуальные методы).
Для описания табличного типа данных в режиме Ideal можно использовать следующий синтаксис:
TABLE имя [элемент_таблицы [,элемент_таблицы.]]
Следующий синтаксис работает только в режиме MASM:
имя TABLE [элемент_таблицы [,элемент_таблицы.]]
Каждое поле "элемент_таблицы" имеет следующий синтаксис:
имя_таблицы
или [VIRTUAL] имя_элемента [[выражение_счетчика_1]] [: сложный_тип [:выражение_счетчика_2]] [=выражение]
где "имя_таблицы" - это имя существующего табличного типа данных, элементы которого целиком встраиваются в определяемую вами табли- цу. Прибегайте к данному синтаксису, если вы хотите использовать наследование.
"Имя_элемента" - это имя элемента таблицы. Необязательное ключевое слово VIRTUAL указывает, что элемент является виртуаль- ным, и ему нужно присвоить смещение.
"Сложный_тип" может представлять собой любое допустимое вы- ражение сложного типа. Описание допустимых типов выражений можно найти в Главе 5.
Если вы не задается поле "сложный_тип", Турбо Ассемблер под- разумевает, что это WORD (или DWORD, если текущей выбранной мо- делью является 32-разрядная модель).

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

foo TABLE VIRTUAL tmp:DWORD:4

определяет элемент таблицы с именем tmp, состоящий из 4 двойных слов.

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

В режиме Ideal имена элементов таблицы являются локальными для таблицы, а в режиме MASM имеют глобальную область действия.

Поле "имя" задает имя табличного типа данных. Позднее вы мо- жете использовать его в модуле для получения информации о таблич- ном типе данных. Для получения информации можно также использо- вать имена отдельных элементов. Подробнее об этом рассказывается в Главе 5.

Табличные типы данных являются переопределяемыми. В модуле вы можете несколько раз определить табличный тип данных с одним и тем же именем. Подробнее об этом рассказывается в Главе 12.

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

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

foo TABLE t1:WORD,t2:WORD,t3:WORD,t4:WORD ; исходная версия

foo TABLE { ; версия, использующая t1:WORD, ; несколько строк

t2:WORD, t3:WORD, t4:WORD }

foo TABLE t1:WORD,t2:WORD, { ; более компактная версия t3:WORD,t4:WORD }

Определение текстовых макрокоманд с помощью директивы EQU

Для определения простых текстовых макрокоманд можно исполь- зовать директиву EQU.Приведем синтаксис определения текстовой ма- крокоманды:
имя EQU текстовая_строка
где "текстовая_строка" связывается с текстовой макрокомандой с именем "имя". Для выделения текста вы можете заключить текстовую строку в угловые скобки (<>), например:
DoneMsg DB <'Returning to DOS'>
Если в режиме MASM вы опустите скобки, то Турбо Ассемблер будет определять, можно ли свести текстовую строку к выражению. Если ее вычислить нельзя, Турбо Ассемблер будет интерпретировать текстовую строку как текстовую макрокоманду (чтобы сохранить совместимость c MASM). Чтобы обеспечить правильное определение текстовой строки, нужно всегда заключать ее в угловые скобки. Рассмотрим следующие ошибки, которые могут возникать, если вы этого не делаете:
Earth EQU dirt ; Earth = "dirt" Planet EQU Earth ; Planet = "dirt" (неверно!) Planet EQU ; Planet = "Earth" (верно!)
В режиме Ideal оператор EQU всегда определяет текстовую мак- рокоманду.
Текстовые макрокоманды являются переопределяемыми. Вы може- те переопределить текстовую макрокоманду в том же модуле другой текстовой строкой.

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

Операторы в теле макрокоманды могут включать в себя операто- ры, вызывающие или определяющие другие макрокоманды. Возьмем сле- дующий пример:
MCREATE MACRO opname, op1,op2,op3,op4,op5,op6,op7 IFNB opname DO & opname MACRO op,count IF count LE 4 REPT count opname op,1 ENDM ELSE MOVE CL,count opname op,CL ENDIF EMDM ; конец DOopname MCREATE op1,op2,op3,op4,op5,op6,op7 ; рекурсия! ENDIF ; конец if ENDM ; конец макрокоманды MCREATE
После вызова:
MCREATE ror,rol,rcl,rcr,shl,sal,sar
будут созданы дополнительные макрокоманды DOror, DOrol и т.д., которые можно использовать следующим образом:
DOshr ax,5 DOrcr bx,3
Рекурсивные макрокоманды можно вызывать со списком парамет- ров и задавать их таким образом, что они будут работать с пере- менным числом параметров (от 0 до максимального числа парамет- ров). Для этого макрокоманда должна использовать первый параметр для выполнения ее макрорасширения, а затем вызывать сама себя с оставшимися параметрами. При каждой рекурсии остается на один параметр меньше. В конце концов, будет последняя рекурсия без па- раметров.
Когда вы вызываете макрокоманду рекурсивно, всегда требуется как-то проверить конец рекурсии. Обычно при наличии передаваемого параметра это делается в теле макрокоманды с помощью условного оператора IFNB. Приведем простой пример рекурсивной макрокоманды:
PUSH MACRO r1,r2,r3,r4,r5,r6,r7,r8 IFNB r1 PUSH r1 PUSH r2,r3,r4,r5,r6,r7,r8 ENDIF ENDM

Определение внешних идентификаторов

Внешние идентификаторы - это идентификаторы, которые опреде- лены вне модуля и которые вы можете использовать внутри модуля. Эти идентификаторы должны быть описаны с помощью директивы PUBLIC. Директива EXTRN имеет следующий синтаксис:
EXTRN определение [,определение] .
где "определение" описывает идентификатор и имеет следующий фор- мат:
[язык] имя [[счетчик_1]] :сложный_тип [:счетчик_2]

Определение записей с битовыми полями

Данные типа записи представляют собой набор битовых полей. Каждое битовое поле имеет заданную длину (в битах) и начальное значение. Размер данных типа записи равен сумме длин всех полей.
Вы можете использовать данные типа записи для максимально компактного представления данных. Например, можно представить группу из 16 флагов (каждый из которых может быть либо установлен (ON), либо сброшен (OFF)) как 16 отдельных бит, 16 отдельных слов, или как запись, содержащую 16 1-битовых полей (наиболее эф- фективный метод).
Приведем синтаксис описания данных типа записи режима Ideal:
RECORD имя [поле_записи [,поле_записи.]]
В режиме MASM этот синтаксис имеет вид:
имя RECORD [поле_записи [,поле_записи.]]
Каждое "поле_записи" имеет следующий синтаксис:
имя_поля : размер_выражения [=значение]
где "имя_поля" - это имя поля записи. Турбо Ассемблер будет выде- лять для него битовое поле размером "размер_выражения". "Значе- ние" и размер выражений не могут быть относительными или иметь опережающие ссылки. Имена поля записи имеют глобальную область действия и переопределяться не могут.
"Имя" - это имя типа записи. Далее вы можете использовать его в модуле для получения различной информации о данных типа за- писи. Вы можете также использовать имена для этого имена отдель- ных полей записи. Значения и выражения размера не могут быть от- носительными и иметь опережающие ссылки. Имена полей записи являются по области действия глобальными и переопределяться не могут.
Вы можете переопределить тип записи и определить в модуле одно и то же имя, как данные типа записи, несколько раз.
Вы можете также использовать имена типов записи для создания переменных и выделения памяти. Подробности можно найти в Главе 12.
Турбо Ассемблер обеспечивает для полей записи специальную поддержку, которая представляет флаги и данные перечислимого ти- па. Более эффективный доступ к полям записи обеспечивают расши- ренные и дополнительные инструкции. Эта концепция описывается в Главе 13.
Для определений данных типа записи, требующих большого числа полей, в Турбо Ассемблере предусмотрен расширенный синтаксис, аналогичный синтаксису перечислимого типа данных. Например, все следующие определения типа данных эквивалентны:
foo RECORD f1:1,f2:2,f3:3,f4:4 ; исходная версия
foo RECORD { ; версия, использующая f1:1, ; несколько строк f2:2, f3:3, f4:4 }
foo RECORD f1:1,f2:2, { ; более компактная версия f3:3,f4:4 }

Определения аргументов и локальных переменных

Турбо Ассемблер передает аргументы процедурам языка высокого уровня в кадре стека, занося аргументы в стек перед вызовом про- цедуры. Когда в процедуре языка требуются аргументы, она считыва- ет их из стека. Когда процедура возвращает управление, она либо удаляет аргументы из стека (соглашения по вызову Паскаля), либо предполагает, что аргументы удаляются из стека вызывающей прог- раммы (соглашения по вызову языка Си).
В описании процедуры передаваемые в процедуру через кадр стека аргументы задаются директивой ARG. Аргументы имеют внутрен- нее представление в виде положительных смещений от регистров BP или EBP.
Языковые соглашения процедуры определяют, будут аргументы заносится в стек в прямом или обратном порядке. В списке аргумен- тов директивы ARG аргументы нужно указывать в том порядке, в ко- тором они указываются в описании процедуры на языке высокого уровня.
Директива LOCAL в описании процедуры задает в кадре стека переменные, локальные для процедуры. Аргументы имеют внутреннее представление в виде отрицательных смещений от регистра BP или EBP.
Выделить пространство для локальных переменных кадра стека можно с помощью включения в процедуру кода инициализации, смещаю- щего вниз указатель стека на нужную величину. Код завершения про- цедуры должен отбрасывать это лишнее пространство, восстанавливая указатель стека. (Когда процедура подчиняется любым языковым сог- лашениям, отличным от NOLANGUAGE, Турбо Ассемблер автоматически генерирует этот код завершения.)
Нужно помнить о том, что Турбо Ассемблер предполагает, что процедура, использующая аргументы кадра стека, содержит соответс- твующий код инициализации, устанавливающий регистр BP или EBP. (Когда процедура подчиняется любым языковым соглашениям, отличным от NOLANGUAGE, Турбо Ассемблер автоматически генерирует этот код завершения.) Даже если процедура использует языковые соглашения NOLANGUAGE, задавайте аргументы и локальные переменные процедуры с помощью директив ARG и LOCAL. Однако в этом случае код начала (код инициализации) и завершения автоматически не генерируется.

Основные моменты в интерфейсе Турбо Ассемблера и Borland C++

Чтобы скомпоновать вместе модули Borland C++ и Турбо Ассемб- лера, должны быть соблюдены следующие три пункта:
1. В модулях Турбо Ассемблера должны использоваться соглаше- ния об именах, принятые в Borland C++.
2. Borland C++ и Турбо Ассемблер должны совместно использо- вать соответствующие функции и имена переменных в форме, приемлемой для Borland C++.
3. Для комбинирования модулей в выполняемую программу нужно использовать утилиту-компоновщик TLINK.
Здесь ничего не говориться о том, что в действительности де- лают модули Турбо Ассемблера. Пока мы коснемся только основных моментов, обеспечивающих разработку функций Турбо Ассемблера, совместимых с С++.

Остаточная рекурсия для методов объектов: инструкция JMP.METHOD

Турбо Ассемблер обеспечивает инструкцию JMP.METHOD, соот- ветствующую инструкции CALL.METHOD. Она имеет следующий син- таксис:
JMP указатель_экземпляра METHOD [имя_объекта:]имя_метода [USES [сегм_регистр:]регистр_смещения]
Инструкция JMP.METHOD полностью аналогична инструкции CALL.METHOD, за исключением того, что она:
- генерирует вместо инструкции CALL инструкцию JMP;
- генерирует код завершения процедуры для очистки стека пе- ред генерацией инструкции JMP.
Инструкция JMP.METHOD позволяет писать эффективный код остаточной рекурсии (tail recursion). Она предназначена для заме- ны общей ситуации, когда инструкция CALL.METHOD дается для конкретного метода с последующей инструкцией RET.

Открытие определения структуры или объединения

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

Отличия режима Ideal и режима MASM

В данном разделе мы опишем основные различия между режимом Ideal и режимом MASM. Если вы знакомы с MASM, то можете поэкспе- риментировать с отдельными средствами, преобразуя небольшие части имеющихся программ в режим Ideal. Более подробно об отличиях ре- жимов рассказывается в Главе 5 "Использование выражений и значе- ний идентификаторов".

Out of hash space

(Не хватает памяти под хеш-таблицы)
Каждому имени идентификатора, определяемому в пользователь- ской программе, соответствует один элемент хеш-таблицы. Эта таб- лица рассчитана на 16384 определяемых пользователем имен иденти- фикаторов при условии запуска Турбо Ассемблера с достаточным объ- емом свободной памяти. Если в программе пользователя определено большее количество имен идентификаторов, то нужно указать пара- метр командной строки /КН, для того чтобы обеспечить в хеш-табли- це нужное число элементов для описания этого количества символи- ческих имен.

Out of memory

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

Out of string space

(Не хватает памяти под строки)
Не хватает оперативной памяти для хранения строк: имен иден- тификаторов, имен файлов, информации для разрешения опережающих ссылок, текстов макрокоманд. Допускается максимум 512К памяти, а ваш модуль превысил этот объем. Попробуйте использовать TASMX.

Параметр /A

Функция: Задает упорядочивание сегментов по алфавиту.
Синтаксис: /A
Примечания: Параметр /A указывает Турбо Ассемблеру, что сег- менты в объектном файле должны быть размещены в алфавитном поряд- ке. Это эквивалентно использование в исходном коде директивы .ALPHA.
Этим параметром обычно приходится пользоваться тогда, когда вы хотите ассемблировать исходный файл, написанный для ранних версий ассемблеров фирм Microsoft или IBM.
Параметр /S изменяет действие данного параметра на обратное, сохраняя используемое по умолчанию последовательное упорядочива- ние сегментов.
Если в исходном файле вы задаете с помощью директивы .SEQ последовательное упорядочивание сегментов, то она отменит дей- ствие параметра /A, задаваемого в командной строке.
Пример:
TASM /A TEST1
Данная командная строка создает объектный файл TEST1.OBJ, сегменты которого упорядочиваются в алфавитном порядке.

Параметр /B

Синтаксис: /B
Примечания: Параметр /B используется в целях совместимости с другими версиями. Он не приводит ни к каким действиям и не оказы- вает влияния на ассемблирование.

Параметр /C

Функция: Разрешает включать в листинг перекрестные ссылки.
Синтаксис: /C
Примечания: Параметр /C разрешает включение в файл листинга информации о перекрестных ссылках. Турбо Ассемблер включает ин- формацию о перекрестных ссылках в таблицу идентификаторов в конце файла листинга. Чтобы получить информацию о перекрестных ссылках, вам нужно также явно задать в командной строке файл листинга или использовать для разрешения формирования файла листинга параметр /L.
Для каждого идентификатора в перекрестных ссылках указывает- ся строка, в которой он определен и все строки, где имеется на него ссылка.
Пример:
TASM /l /c TEST1
Данная команда создает файл листинга, в таблице идентифика- торов которого содержится информация о перекрестных ссылках.

Параметр /D

Функция: Определяет идентификатор.
Синтаксис: /Dидентификатор[=значение или выражение]
Примечания: Параметр /D определяет идентификатор для исход- ного файла, точно также, как если бы он определялся на первой строке исходного файла с помощью директивы =. В командной строке этот параметр можно использовать любое число раз.
Вы можете только определить идентификатор, равный другому идентификатору, или постоянному значению. Справа от знака ра- венства (=) не допускается использовать выражение с операциями. Например, допустимо /DX=9 и /DX=Y, но параметр /DX=Y-4 не допус- кается.
Пример:
TASM /DMAX=10 /DMIN=2 TEST1
В данной командной строке определяются два идентификатора MAX и MIN, на которые могут ссылаться другие операторы в исходном файле TEST1.ASM.

Параметр /E

Функция: Генерирует инструкции эмуляции работы с плавающей точкой.
Синтаксис: /E
Примечания: Параметр /E указывает Турбо Ассемблеру, что нуж- но генерировать инструкции работы с плавающей точкой, которые бу- дут выполняться с помощью программного обеспечения (эмулятора операций с плавающей точкой). Используйте этот параметр, если ваша программа содержит библиотеку эмуляции работы с плавающей точкой, которая эмулирует функции арифметического сопроцессора 80х87.
Обычно этот параметр следует использовать только в том слу- чае, если ваш модуль на Ассемблере является частью программы, на- писанной на языке высокого уровня, в которой используется библио- тека эмуляции работы с плавающей точкой (эмуляцию операций с пла- вающей точкой поддерживают компиляторы Borland C++, Турбо Си, Турбо Паскаль, Турбо Бейсик и Турбо Пролог). Вы не можете просто скомпоновать программу на Ассемблере с библиотекой эмуляции, так как предполагается, что библиотека должна инициализироваться на- чальным кодом компилятора.
Параметр /R изменяет действие данного параметра на обратное, разрешая ассемблирование действительных инструкций с плавающей точкой, которые могут выполняться арифметическим сопроцессором.
Если в исходной файле вы используете директиву NOEMUL, то она отменит действие параметра /E в командной строке.
Параметр командной строки /E оказывает то же действие, что и использование в начале исходного файла директивы EMUL, и эквива- лентно параметру командной строки /JEMUL.
Пример:
TASM /E SEGANT TCC -f TRIG.C SEGANT.OBJ
Первая командная строка ассемблирует модуль в эмулируемыми инструкциями с плавающей точкой. Вторая командная строка компили- рует модуль языка Си с эмуляцией операций с плавающей точкой и затем компонует его с объектным файлом Ассемблера.

Параметр /H или /?

Функция: Выводит на экран дисплея справочную информацию.
Синтаксис: /H или /?
Примечания: Параметр /H указывает Турбо Ассемблеру, что на экран дисплея нужно вывести справочную информацию, описывающую синтаксис командной строки. Эта справочная информация включает в себя список параметров, а также различные задаваемые имена фай- лов. Параметр /? делает то же самое.
Пример:
TASM /h

Параметр /I

Функция: Задает маршрут доступа к включаемому файлу.
Синтаксис: /Iмаршрут
Примечания: Параметр /I указывает Турбо Ассемблеру, где нуж- но искать файлы, включаемые в исходный файл по директиве INCLUDE. В командной строке можно указать несколько параметров /I (их чис- ло ограничено только размерами оперативной памяти).
Когда Турбо Ассемблер обнаруживает директиву INCLUDE, то место, где он будет искать включаемый файл определяется тем, яв- ляется ли имя файла в директиве INCLUDE маршрутом доступа к ката- логу, или это просто имя файла.
Если вы в качестве части имени файла указываете маршрут, то сначала делается попытка поиска по данному маршруту, а затем Тур- бо Ассемблер выполняет поиск в каталогах, заданных в параметрах командной строки /I (в том порядке, как они указаны в командной строке). Затем он ищет файл по всем каталогам, заданным в пара- метрах /I файла конфигурации.
Если в спецификации имени файла вы не указываете маршрут, то Турбо Ассемблер выполняет сначала поиск в каталогах, заданных в параметрах командной строки /I, затем - в каталогах, заданных в параметрах /I файла конфигурации, и, наконец, в текущем каталоге.
Пример:
TASM /I\INCLUDE /ID:\INCLUDE TEST1
Если исходный файл содержит оператор:
INCLUDE MYMACS.INC
то Турбо Ассемблер сначала ищет файл \INCLUDE\MYMACS.INC, затем D:\INCLUDE\MYMACS.INC. Если он еще не нашел файл, то файл с именем MYMACS.INC ищется в текущем каталоге. Если бы в исходном файле содержался оператор:
INCLUDE INCS\MYMACS.INC
то Турбо Ассемблер сначала искал бы включаемый файл \INCS\MYMACS.INC, затем \INCLUDE\MYMACS.INC, и, наконец D:\INCLUDE\MYMACS.INC.

Параметр /J

Функция: Определяет директиву инициализации Ассемблера.
Синтаксис: /Jдиректива
Примечания: Параметр /J позволяет вам определить директиву, которая будет ассемблироваться перед первой строкой исходного файла. "Директива" может представлять собой любую директиву Турбо Ассемблера, не требующую аргументов, например, .286, IDEAL, %MACS, NOJUMP и т.д. Полное описание директив Турбо Ассемблера содержится в соответствующей главе.
В командной строке вы можете указать более одного параметра /J. При этом они будут обработаны слева направо.
Пример:
TASM /J.286 .JIDEAL TEST1
При этом ассемблируется файл TEST1.ASM с разрешенными инст- рукциями процессора 80286 и разрешением синтаксического анализа выражений в режиме IDEAL.

Параметр /KH

Функция: Задает максимально допустимое число идентификато- ров.
Синтаксис: /KHnидентификаторов
Примечания: Параметр /KH задает максимально допустимое число идентификаторов, которое может содержать программа. Если вы не используете данный параметр, ваша программа может содержать толь- ко до 8192 идентификаторов. Использование этого параметра позво- ляет увеличить число идентификаторов до значения "nидентификато- ров" (это значение не должно превышать 32768).
Используйте данный параметр, если при ассемблировании прог- раммы вы получаете сообщение "Out of hash space" ("Буферное пространство исчерпано").
Данный параметр можно также использовать для уменьшения об- щего числа идентификаторов до значения, меньшего назначенного по умолчанию (8192). Это позволит освободить некоторое количество памяти, что может оказаться полезным, когда вы пытаетесь ассем- блировать программу, а у вас не хватает памяти.
Пример:
TASM /KH10000 BIGFILE
Эта команда сообщает Турбо Ассемблеру, что при ассемблирова- нии файла BIGFILE нужно зарезервировать память для 10000 иденти- фикаторов.

Параметр /L

Функция: Генерирует файл листинга.
Синтаксис: /L
Примечания: Параметр /L указывает, что вы хотите создать файл листинга, даже если вы его не задаете в командной строке явно. Файл листинга имеет то же имя, что и исходный файл, и рас- ширение .LST.
Пример:
TASM /L TEST1
Данная командная строка приводит к созданию файла листинга с именем TEST1.LST.

Параметр /LA

Функция: Показывает в исходной файле код интерфейса с языком высокого уровня.
Синтаксис: /LA
Примечания: Параметр /LA указывает Турбо Ассемблеру, что в файле листинга нужно отразить весь генерируемый код, включая код, который генерируется в результате директивы языка высокого уровня .MODEL.
Пример:
TASM /LA FILE1

Параметр /M

Функция: Устанавливает максимальное число проходов Ассембле- ра.
Синтаксис: /M[число_проходов]
Примечания: Обычно Турбо Ассемблер работает как однопроход- ный Ассемблер. Параметр /m позволяет задать максимальное число проходов, которое Ассемблер выполнит в процессе ассемблирования. TASM автоматически определяет те случаи, когда фактически требуе- мое число проходов меньше заданного. Если максимальное число про- ходов не задано, то по умолчанию оно равно пяти.
Некоторые модули могут содержать конструкции, которые будут правильно ассемблироваться только при двух проходах, либо вы можете захотеть удалить инструкции NOP, добавленные Ассемблером из-за опережающих ссылок. Если множественные проходы не разреше- ны, то для такого модуля будет выдано хотя бы одно предупреждение "Pass-dependent construction encountered" ("Встречена конструк- ция, зависящая от числа проходов"). При заданном параметре /m Турбо Ассемблер сможет ассемблировать такой код, но не сможет его оптимизировать за счет удаления пустых команд NOP, независимо от числа заданных проходов. В этом случае будет выдано предупрежде- ние "Module is pass dependent - compatibility pass was done" ("Модуль зависим от числа проходов - сделан проход для обеспече- ния совместимости").
Пример:
TASM /M2 TEST1
Эта строка заставит Турбо Ассемблер при ассемблировании программы TEST1 выполнить два прохода.

Параметр /ML

Функция: Интерпретирует различие в регистрах букв идентифи- каторов.
Синтаксис: /ML
Примечания: Параметр /ML указывает Турбо Ассемблеру, что во всех идентификаторах нужно различать буквы разного регистра (строчные и прописные). Обычно строчные и прописные буквы рас- сматриваются, как эквивалентные, поэтому имена ABCxyz, ABCXYZ и abcxyz обозначают один и тот же идентификатор. Если вы задаете параметр /ML, то эти три идентификатора будут считаться различны- ми. Тем не менее, даже после задания параметра /ML ключевые слова Ассемблера можно вводить как в верхнем, так и в нижнем регистре. Ключевые слова представляют собой идентификаторы, встроенные в Ассемблер, которые имеют специальное значение (мнемоники инструк- ций, директивы и операторы).
Пример:
TASM /ML TEST1
где TEST1.ASM содержит следующие операторы:
ABC DW 1 abc DW 0 ; это не дублирующий идентификатор Mov Ax,[Bp] ; в ключевых словах допускается использо- ; вать разный регистр
Для модулей Паскаля параметр-переключатель /ml при использо- вании его совместно с параметром /mx имеет специальное значение. Подробнее об этом рассказывается в описании параметра /mx.

Параметр /MU

Функция: Преобразует идентификаторы в верхний регистр.
Синтаксис: /MU
Примечания: Параметр /MU указывает Ассемблеру, что нужно иг- норировать регистр во всех идентификаторах. По умолчанию в Турбо Ассемблере задано, что в идентификаторах все буквы нижнего ре- гистра должны преобразовываться в верхний регистр (если это не отменено с помощью директивы /ML).
Пример:
TASM /MU TEST1
При этом все идентификаторы будут преобразованы в верхний регистр (что задано по умолчанию):
EXTRN myfunc:NEAR call myfunc ; не важно, как была ; определена функция: ; MYFUNC, Myfunс,.

Параметр /MV#

Функция: Устанавливает максимальную длину имен идентификато- ров.
Синтаксис: /MV#
Примечания: Параметр /mv# устанавливает максимальную длину распознаваемых TASM имен идентификаторов. Например, если задать /mv12, то TASM будет рассматривать имена ABCDEFGHIJKLL. и ABCDEFGHIJKL как одно и то же имя ABCDEFGHIJKLL. Заметим, что ми- нимальное значение, которое вы здесь можете задавать, равно 12.

Параметр /MX

Функция: Задает различимость на на строчные и прописные бук- вы (верхний и нижний регистр) во внешних и общедоступных иденти- фикаторах.
Синтаксис: /MX
Примечания: Параметр /MX сообщает Турбо Ассемблеру, что раз- личать регистр букв нужно только во внешних (External) и общедос- тупных (Public) идентификаторах. Все другие идентификаторы в ис- ходном файле будут интерпретироваться, как набранные в верхнем регистре.
Использовать данную директиву следует при вызове процедур из других модулей, которые ассемблировались или компилировались так, что сохранилось различие в строчных и прописных буквах (например, модулей, которые компилировались в Borland C++).
Пример:
TASM /MX TEST1
где TEST1 содержит следующие исходные строки:
EXTRN Cfunc:NEAR myproc PROC NEAR call Cfunc . . .
Замечание: Использование вместе параметров /mx и /ml для идентификаторов, описанных в Паскале, имеет специальное значение. Если вы используете эти параметры вместе, иденти- фикаторы будут доступны компоновщику, как символы в верхнем регистре.

Параметр /N

Функция: Подавляет в файле листинга таблицу идентификаторов.
Синтаксис: /N
Примечания: Параметр /N показывает, что в конце файла лис- тинга вы не хотите использовать обычную таблицу идентификаторов. Обычно в конце файла листинга содержится полная таблица идентифи- каторов, где показаны все идентификаторы, их имена и значения.
Вы должны задать файл листинга либо явным образом (в коман- дной строке), либо с помощью параметра /L. В противном случае па- раметр /N не приведет ни к каким действиям.
Пример:
TASM /L /N TEST1
При этом генерируется файл листинга, где показывается только генерируемый код без значений ваших идентификаторов.

Параметр /O

Функция: Генерирует оверлейный код.
Синтаксис: /O
Примечания: Задание параметра командной строки /o вызывает генерацию оверлейно-совместимых адресов фиксации. При ее исполь- зовании ссылки 386 к сегментам USE32 не должны выполняться, так как это может привести к неправильной работе компоновщика.

Параметр /OP

Функция: Генерирует оверлейный код для компоновщика Phar Lap.
Синтаксис: /OP
Примечание: Задание параметра командной строки /o вызывает генерацию оверлейно-совместимых адресов фиксации для компоновщика Phar Lap. Полученный объектный файл не будет совместимым с компо- новщиком фирмы Borland TLINK.

Параметр /P

Функция: Проверяет наличие "кода с побочными эффектами" в защищенном режиме.
Синтаксис: /P
Примечания: Параметр /P определяет, что вы хотите получить предупреждение при любой инструкции, генерирующей в защищенном режиме код с возможным "побочным эффектом" (impure code). Инс- трукции, перемещающие данные в память путем переопределения ре- гистра CS: в защищенном режиме рассматриваются, как некорректные, поскольку они в защищенном режиме могут работать неверно, если не принять специальных мер.
Этот параметр нужно использовать только в том случае, если вы пишете программу, выполняемую на процессоре 80286, i486 или 80386 в защищенном режиме.
Пример:
TASM /P TEST1
где TEST1 содержит следующие операторы:
.286P CODE SEGMENT temp DW ? mov CS:temp,0 ; в защищенном режиме может выпол- ; няться некорректно

Параметр /Q

Функция: Подавляет вывод записей объектного файла (.OBJ), ненужных для компоновки.
Синтаксис: /Q
Примечания: Параметр /q удаляет из результирующего объектно- го файла записи об авторских правах и записи зависимости файла, уменьшая тем самым его размер. Этот параметр не следует задавать при использовании программы MAKE или аналогичной программы, рабо- та которой основывается на записях зависимостей.

Параметр /R

Функция: Генерирует реальные инструкции с плавающей точкой.
Синтаксис: /R
Примечания: Параметр /R указывает Турбо Ассемблеру, что нуж- но генерировать реальные инструкции с плавающей точкой (вместо генерации эмулируемых инструкций с плавающей точкой). Используйте этот параметр, если вы хотите выполнять свою программу на маши- нах, оснащенных арифметическим сопроцессором 80х87.
Действие данного параметр изменяет на обратное параметр /E (при этом генерируются эмулируемые инструкции с плавающей точ- кой).
Если в исходном файле вы используете директиву EMUL, то она отменит действие инструкции /R, указанной в командной строке.
Параметр командной строки /R имеет тот же эффект, что и ис- пользование в начале исходного файле директивы NOEMUL и совпадает с действием параметра командной строки /JNOEMUL.
Пример:
TASM /R SEGANT TPC /$N+ /$E- TRIG.PAS
Первая команда ассемблирует модуль с реальными инструкциями с плавающей точкой. Вторая командная строка компилирует исходный модуль Паскаля с реальными инструкциями с плавающей точкой, кото- рый компонуется с объектным файлом Ассемблера.

Параметр /S

Функция: Задает последовательное упорядочивание сегментов.
Синтаксис: /S
Примечания: Параметр /S указывает Турбо Ассемблеру, что сег- менты в объектном файле нужно разместить в том порядке, в котором Турбо Ассемблер обнаруживает их в исходном коде. По умолчанию Турбо Ассемблер использует именно такое упорядочивание сегментов, если вы не изменили его с помощью параметра /A в командной строке или в файле конфигурации.
Если с помощью директивы .ALPHA в исходном коде вы задали упорядочивание сегментов в алфавитном порядке, то эта директива отменит параметр /S, задаваемый в командной строке.
Пример:
TASM /S TEST1
По данной команде создается объектный файл (TEST1.OBJ), сег- менты которого упорядочены в том порядке, как они содержатся в исходном файле.

Параметр /T

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

Параметр /V

Синтаксис: /V
Примечания: Параметр /V используется в целях совместимости. Он не приводит ни к каким действиям и не оказывает влияния на ас- семблирование.

Параметр /W

Функция: Управляет генерацией предупреждающих сообщений.
Синтаксис: /W W-[класс_предупреждений] W+[класс_предупреждений]
Примечания: Параметр /W управляет выводом Турбо Ассемблером предупреждающих сообщений.
Если вы просто укажете параметр /W, то будут выводиться "слабые" предупреждения. Такие предупреждения показывают, что вы можете несколько улучшить эффективность вашей программы.
Если вы зададите параметр /W- без класса предупреждений, то все предупреждения запрещаются. Если за параметром указывается класс предупреждений, то запрещаются только эти предупреждения. Каждое предупреждающее сообщение имеет идентификатор из трех букв:
ALN - выравнивание сегмента в памяти. ASS - подразумевается использование 16-разрядного сегмента; BRK - требуются квадратные скобки; ICG - неэффективная генерация кода; LCO - переполнение счетчика адреса; OPI - открытый блок условия IF; OPP - открытая процедура; OPS - открытый сегмент; OVF - арифметическое переполнение; PDC - конструкция, зависящая от прохода; PQK - предполагается константа для предупреждения [const]. PRO - запись в память в защищенном режиме требует переопределения регистра CS. RES - предупреждение о резервируемом слове. TPI - предупреждение о недопустимости в Турбо Паскале.
Если вы указываете параметр /W+ без класса предупреждения, то все предупреждения будут разрешены. Если вы задаете параметр /W+ с классом предупреждений из предыдущего списка, то будут раз- решены только эти предупреждения.
По умолчанию Турбо Ассемблер сначала начинает ассемблирова- ние исходного файла с разрешением всех предупреждений, кроме пре- дупреждений о неэффективности кода (ICG) и предупреждений и запи- си в память в защищенном режиме (PRO).
Для управления выводом определенных сообщений на заданном участке программы в файле с исходным кодом вы можете использовать директивы WARN или NOWARN. Более подробно об этих директивах рас- сказывается в главе, посвященной директивам.
Пример:
TASM /W TEST1
Следующий оператор в TEST1.ASM выведет предупреждающее сооб- щение, которое не появится на экране, если не указан параметр /W:
mov bx,ABC ; предупреждение о неэффективности кода ABC = 1
При задании командной строки:
TASM /W-OVF TEST2
если TEST2.ASM содержит:
DW 1000h = 20h
предупреждения генерироваться не будут.

Параметр /X

Функция: Включает в листинг блоки условного ассемблирования.
Синтаксис: /X
Примечания: Если при вычислении блоков IF, IFNDEF, IFDEF и т.д. получается значение FALSE, то параметр /X приводит к тому, что операторы, содержащиеся внутри условного блока, будут включе- ны в листинг ассемблирования. По данной директиве в листинг будут также включены сами директивы условного ассемблирования (обычно они в листинг не включаются).
Вы должны в командной строке или с помощью параметра /L за- дать также необходимость генерации файла листинга, иначе параметр /X действовать не будет.
Для переопределения параметра /x вы можете использовать ди- рективы .LFCOND, .SFCOND и .TFCOND.
Пример:
TASM /X TEST1

Параметр /Z

Функция: Выводит на экран наряду с сообщениями об ошибке со- ответствующие строки исходного текста.
Синтаксис: /Z
Примечания: Параметр /Z указывает Ассемблеру, что при гене- рации сообщения об ошибке на экран нужно вывести соответствующую строку исходного файла (где эта ошибка возникла). Вызвавшая ошиб- ку строка выводится перед сообщением об ошибке. При запрещении данного параметра Турбо Ассемблер просто выводит сообщение, опи- сывающее ошибку.
Пример:
TASM /Z TEST1

Параметр /ZD

Функция: Разрешает включение в объектные файлы информации о номерах строк.
Синтаксис: /ZD
Примечания: Параметр /ZD приводит к тому, что Турбо Ассемб- лер будет помещать в объектные файлы информацию о номерах строк. Это позволяет автономному отладчику фирмы Borland (Турбо отладчи- ку) выводить на экран текущее место в исходном коде, но не позво- ляет ему осуществлять доступ к элементам данных.
Если при попытке отладки программы с помощью Турбо отладчика вам не хватит памяти, вы можете использовать параметр /ZD для од- них модулей и параметр /ZI - для других.
Пример:
TASM /ZD TEST1

Параметр /ZI

Функция: Разрешает включение в объектный файл информации для отладки.
Синтаксис: /ZI
Примечания: Параметр /ZI указывает Турбо Ассемблеру, что в объектный файл нужно вывести полную информацию для отладки. Эта информация включает в себя записи о номерах строк (для синхрони- зации вывода на экран исходного текста) и информацию о типах дан- ных, позволяющую модифицировать и проверить данные программы.
Параметр /ZI позволяет вам использовать все средства Турбо отладчика для прохождения программы и проверки и изменения эле- ментов данных. Вы можете использовать параметр /ZI для всех моду- лей программы или только для тех, отладка которых вас интересует. Поскольку параметр /ZI добавляет информацию в объектные и выпол- няемые файлы, может оказаться нежелательным его использование для всех модулей программы при выполнении программы Турбо отладчиком (например, может возникать ситуация нехватки памяти).
Пример:
TASM /ZI TEST1

Параметр /ZN

Функция: Запрещает включение в объектный файл информацию для отладки.
Синтаксис: /ZN
Примечания: Параметр /zn указывает Турбо Ассемблеру, что ин- формацию для отладки в объектный файл выводить не нужно. Его по- лезно использовать для переопределения параметра /zi в файле кон- фигурации.

Параметры изменения формата листинга

Директивы управления форматом листинга изменяют формат файла листинга. Вы можете использовать эти директивы, чтобы приспосо- бить вид листинга под свой вкус и потребности.
Директива PAGE задает высоту и ширину страницы файла листин- га и начинает новую страницу. Директива PAGE работает только в режиме MASM. Она имеет следующий синтаксис:
PAGE [число_строк] [,число_столбцов] PAGE +
где "число_строк" задает число строк, выводимых на странице лис- тинга. Поле "число_столбцов" может принимать значения от 59 до 255 и задает число столбцов на странице. Если вы опустите один из этих параметров, то текущая установка данного параметра остается без изменений. Чтобы изменить только число столбцов, укажите пе- ред этим параметром запятую, в противном случае вы измените число строк.
Если вы укажете после директивы PAGE символ плюса (+), то начинается новая страница, номер раздела увеличивается, а номер страницы снова начинается с 1. Если вы используете директиву PAGE без аргументов, то листинг возобновляется с новой страницы без изменения номера раздела.
Директива %PAGESIZE работает также, как директива PAGE, но она не начинает новую страницу и работает как в режиме Ideal, так и в режиме MASM. Директива %PAGESIZE имеет следующий синтаксис:
%PAGESIZE [число_строк] [,число_столбцов]
Директива %NEWPAGE работает как директива PAGE без аргумен- тов. Строки исходного текста после директивы %NEWPAGE будут начи- наться в файле листинга с новой страницы. Директива %PAGESIZE имеет следующий синтаксис:
%NEWPAGE
Директива %BIN устанавливает длину поля объектного кода в файле листинга. Директива %BIN имеет синтаксис:
%BIN размер
где "размер" является константой. Если вы не используете эту ди- рективу, то поле кода операции занимает в файле листинга до 20 позиций, например:
%BIN 12 ; устанавливает 12 позиций в листинге
Директива %DEPTH устанавливает размер поля глубины в файле листинга. Эта директива имеет следующий синтаксис:
%DEPTH размер
где "размер" задает, скольку столбцов нужно зарезервировать в по- ле глубины файла листинга. Это поле указывает уровень вложенности включаемых файлов (INCLUDE) и макрорасширений. Если в качестве размера вы укажете 0, то это поле в файле листинга не выводится. Обычно не требуется задавать "размер" > 2, поскольку при этом без усечения будет выводиться глубина до 99. По умолчанию это поле имеет значение 1.

Директива % LINENUM задает размер поля номеров строк в файле листинга. Данная директива имеет следующий синтаксис:
%LINENUM размер
Директива %LINENUM позволяет вам задать, сколько позиций должны занимать номера строк в файле листинга. В поле "размер" должна указываться константа. Если вы хотите минимизировать шири- ну листинга, то можете уменьшить размер поля номеров строк. Кроме того, если ваш файл содержит более 9999 строк, то размер этого поля может потребоваться увеличить, чтобы номера не усекались. По умолчанию номер строки занимает 4 столбца.
Директива %TRUNC усекает слишком длинные поля листинга. Она имеет следующий синтаксис:
%TRUNC
Поле объектного кода в файле листинга имеет достаточно мес- та для вывода кода, генерируемого большинством инструкций или ди- рективами выделения данных. Размер этого поля можно настроить с помощью директивы %BIN. Если отдельная строка исходного кода ге- нерирует больше кода, чем можно вывести на одной строке, то оста- ток автоматически усекается и таким образом будет невидимым. Если вы хотите видеть весь генерируемый код, то используйте директиву %NOTRUNC (слишком длинная строка при этом будет переноситься на следующую). В противном случае используйте директиву %TRUNC. Вы можете использовать эти инструкции для переключения режима усече- ния.
Директива %NOTRUNC имеет следующий синтаксис:
%NOTRUNC
Директива %PCNT задает размер поля "сегмент:смещение" в фай- ле листинга. Она имеет следующий синтаксис:
%PCNT размер
где "размер" - это число столбцов, которые вы хотите зарезервиро- вать для смещения в текущем ассемблируемом сегменте. Турбо Ас- семблер устанавливает размер, равный 4, для обычных 16-битовых сегментов и 8 для 32-битовых сегментов процессора 80386. Директи- ва %PCNT позволяет переопределить эти используемые по умолчанию значения.
Директива TITLE, которую вы можете использовать только в ре- жиме MASM, задает заголовок файла листинга. Эта директива имеет следующий синтаксис:
TITLE текст


Заголовок "текст" будет выводиться в верхней части каждой страницы после имени исходного файла и перед заголовком, заданным по директиве SUBTTL. Директиву TITLE можно использовать в прог- рамме только один раз.
Директива %TITLE работает также, как директива TITLE, но вы можете использовать ее как в режиме MASM, так и в режиме Ideal. Эта директива имеет следующий синтаксис:
%TITLE "текст"
Директива SUBTTL работает только в режиме MASM и задает под- заголовок файла листинга. Она имеет следующий синтаксис:
SUBTTL текст
Подзаголовок выводится в верхней части каждой страницы после имени исходного файла и после заголовка, заданного директивой TITLE.
Директиву SUBTTL можно указывать в программе столько раз, сколько требуется. Каждая директива изменяет подзаголовок, кото- рый будет выводиться на следующей странице листинга.
Директива %SUBTTL работает аналогично директиве SUBTTL, но ее можно указывать как в режиме Ideal, так и в режиме MASM. Эта директива имеет синтаксис:
%SUBTTL "текст"
Директива %TABSIZE задает позицию табуляции в файле листин- га. Данная директива имеет следующий синтаксис:
%TABSIZE размер
где "размер" - это число столбцов между двумя позициями табуляции в файле листинга. По умолчанию это 8 столбцов.
Для задания длины поля исходного текста в файле листинга можно использовать директиву %TEXT, которая имеет синтаксис:
%TEXT размер
где "размер" - это число столбцов, используемых для вывода в фай- ле листинга исходных строк. Если размер исходной строки превышает длину этого поля, то оно будет либо усекаться, либо переноситься на следующую строку, в зависимости от использования директивы %TRUNC или %NOTRUNC.
Чтобы сохранить все параметры управления листингом в 16-уровневом стеке, можно использовать директиву %PUSHCTL. Она сохраняет только те параметры управления листингом, которые можно разрешить или запретить (%INCL, %NOINCL и т.д.). Размеры полей листинга не сохраняются. Данная директива особенно полезна в мак- рокомандах, когда вы можете вызывать специальные режимы листинга, которые отменяются при завершении макрорасширения.
Директива %PUSHCTL имеет синтаксис:
%PUSHCTL
Директива %POPCTL восстанавливает из стека параметры управ- ления листингом и имеет следующий синтаксис:
%POPCTL
Эта директива восстанавливает параметры управления листингом в те значения, которые они имели перед последней директивой %PUSHCTL. Значения размеров полей листинга (%DEPTH, %PCNT и т.д.) не восстанавливаются.
Назад | Содержание | Вперед

Параметры командной строки

Необязательные параметры командной строки позволяют вам уп- равлять поведением Ассемблера и тем, какую информацию он выводит на экран, в листинг и объектный файл. В Турбо Ассемблере предус- мотрены некоторые параметры, которые не выполняют никаких дей- ствий, а используются только для совместимости текущей версии TASM с предыдущими версиями MASM (макроассемблер фирмы Microsoft):
 1  0/b Задает размер буфера  1  0/v Выводит на экран дополнительную статистику
Вы можете задавать параметры, представляющие собой любую комбинацию букв в верхнем и нижнем регистре. Кроме того, парамет- ры можно задавать в любом порядке (кроме параметров /I и /J), они будут при этом обрабатываться последовательно. При использовании параметра /d нужно быть внимательным: идентификаторы надо опреде- лить до того, как они будут использованы в последующих параметрах /d.
Примечание: С помощью директив, указанных в исходном коде, вы можете отменить эквивалентные им параметры Ассемб- лера.
На Рис. 2.1 (см. выше) приведен полный список параметров Турбо Ассемблера. Далее эти параметры описаны подробно.

Параметры-переменные

Все параметры-переменные (var) передаются точно также: как указатель дальнего типа на их действительные адреса в памяти.

Параметры-значения

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

Pass-dependant construction encountered

(Обнаружена конструкция, зависящая от прохода)
Действие оператора возможно не совпадает с ожидаемым из-за однопроходности Турбо Ассемблера. Например:
IF1 ; на шаге ассемблирования ENDIF IF2 ; на шаге листинга ENDIF
Большинство конструкций, с которыми связано это сообщение, может быть скорректировано так, чтобы это сообщение исчезло. Обычно для этого достаточно убрать ссылки вперед.

Передача параметров

Borland C++ передает функциям параметры через стек. Перед вызовом функции С++ сначала заносит передаваемые этой функции па- раметры, начиная с самого правого параметра и кончая левым, в стек. В С++ вызов функции:
. . . Test(i, j, 1); . . .
компилируется в инструкции:
mov ax,1 push ax push word ptr DGROUP:_j push word ptr DGROUP:_i call near ptr _Test add sp,6
где видно, что правый параметр (значение 1), заносится в стек первым, затем туда заносится параметр j и, наконец, i.
При возврате из функции занесенные в стек параметры все еще находятся там, но они больше не используются. Поэтому непосредс- твенно после каждого вызова функции Borland C++ настраивает ука- затель стека обратно в соответствии со значением, которое он имел перед занесением в стек параметров (параметры, таким образом, от- брасываются). В предыдущем примере три параметра (по два байта каждый) занимают в стеке вместе 6 байт, поэтому Borland C++ до- бавляет значение 6 к указателю стека, чтобы отбросить параметры после обращения к функции Test. Важный момент здесь заключается в том, что в соответствии с используемыми по умолчанию соглашениями Си/C++ за удаление параметров из стека отвечает вызывающая прог- рамма.
Функции Ассемблера могут обращаться к параметрам, передавае- мым в стеке, относительно регистра BP. Например, предположим, что функция Test в предыдущем примере представляет собой следующую функцию на Ассемблере (PRMSTACK.ASM):
.MODEL SMALL .CODE PUBLIC _Test _Test PROC push bp mov bp,sp mov ax,[bp+4] ; получить параметр 1 add ax,[bp+6] ; прибавить параметр 2 ; к параметру 1 sub ax,[bp+8] ; вычесть из суммы 3 pop bp ret _Test ENDP
Как можно видеть, функция Test получает передаваемые из программы на языке Си параметры через стек, относительно регистра BP. (Если вы помните, BP адресуется к сегменту стека.) Но откуда она знает, где найти параметры относительно BP?
На Рис. 18.2 показано, как выглядит стек перед выполнением первой инструкции в функции Test:
i = 25; j = 4; Test(1, j, 1);

. . . . . . ¦ ¦ +-----------------------+ ¦ ¦ +-----------------------+ SP -- ¦ Адрес возврата ¦ +-----------------------+ SP + 2 ¦ 25 (i) ¦ +-----------------------+ SP + 4 ¦ 4 (j) ¦ +-----------------------+ SP + 6 ¦ 1 ¦ +-----------------------+ ¦ ¦ +-----------------------+ ¦ ¦ . . . . . .

Рис. 18. 2 Состояние стека перед выполнением первой инструк- ции функции Test

Параметры функции Test представляют собой фиксированные ад- реса относительно SP, начиная с ячейки, на два байта старше адре- са, по которому хранится адрес возврата, занесенный туда при вы- зове. После загрузки регистра BP значением SP вы можете обращать- ся к параметрам относительно BP. Однако, вы должны сначала сохра- нить BP, так как в вызывающей программе предполагается, что при возврате BP изменен не будет. Занесение в стек BP изменяет все смещения в стеке. На Рис. 18.3 показано состояние стека после вы- полнения следующих строк кода:

. . . push bp mov bp,sp . . .

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

Рис. 18.3 Состояние стека после инструкций PUSH и MOVE

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

Пространство для динамических локальных переменных можно за- резервировать, вычитая из SP требуемое число байт. Например, пространство для динамического локального массива размером в 100 байт можно зарезервировать, если начать функцию Test с инструк- ций:


. . . push bp mov bp,sp sub sp,100 . . .

как показано на Рис. 18.4

. . . . . . ¦ ¦ +-----------------------+ SP -- ¦ ¦ - BP - 100 +-----------------------+ ¦ ¦ +-----------------------+ . . . . . . . . ¦ ¦ +-----------------------+ SP + 100 -- ¦ BP вызывающей прогр. ¦ -- BP +-----------------------+ SP + 102 ¦ Адрес возврата ¦ BP + 2 +-----------------------+ SP + 104 ¦ 25 (i) ¦ BP + 4 +-----------------------+ SP + 106 ¦ 4 (j) ¦ BP + 6 +-----------------------+ SP + 108 ¦ 1 ¦ BP + 8 +-----------------------+ ¦ ¦ +-----------------------+ ¦ ¦ . . . . . .

Рис. 18. 4 Состояние стека после инструкций PUSH, MOVE и SUB

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

mov byte ptr [bp-100]

даст значение первого байта ранее зарезервированного 100-байтово- го массива. При передаче параметров всегда используется положи- тельная адресация относительно регистра BP.

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

LOCAL LocalArray:BYTE:100,LocalCount:WORD=AUTO_SIZE

определяет динамические переменные LocalArray и LocalCount. LocalArray на самом деле представляет собой метку, приравненную к [BP-100], а LocalCount - это метка, приравненная к [BP-102]. Од- нако вы можете использовать их, как имена переменных. При этом вам даже не нужно будет знать их значения. AUTO_SIZE - это общее число байт (объем памяти), необходимых для хранения динамических локальных переменных. Чтобы выделить пространство для динамичес- ких локальных переменных, это значение нужно вычесть из SP.


Приведем пример того, как нужно использовать директиву LOCAL:

. . . _TestSub PROC LOCAL LocalArray:BYTE:100,LocalCount:WORD=AUTO_SIZE push bp ; сохранить указатель стека ; вызывающей программы mov bp,sp ; установить собственный ; указатель стека sub sp,AUTO_SIZE ; выделить пространство для ; динамических локальных ; переменных mov [LocalCount],10 ; установить переменную ; LocalCount в значение 10 ; (LocalCount это [BP-102]) . . . mov cx,[LocalCount] ; получить значение ; (счетчик) из локальной ; переменной mov al,'A' ; заполним символом 'A' lea bx,[LocalArray] ; ссылка на локальный ; массив LocalArray ; (LocalArray это [BP-100]) FillLoop: mov [bx],al ; заполнить следующий байт inc bx ; ссылка на следующий байт loop FillLoop ; обработать следующий байт, ; если он имеется mov sp,bp ; освободить память, ; выделенную для динамичес- ; ких локальных переменных ; (можно также использовать ; add sp,AUTO_SIZE) pop bp ; восстановить указатель ; стека вызывающей программы

ret _TestSub ENDP . . .

В данном примере следует обратить внимание не то, что первое поле после определения данной динамической локальной переменной представляет собой тип данных для этой переменной: BYTE, WORD, DWORD, NEAR и т.д. Второе поле после определения данной динами- ческой локальной переменной - это число элементов указанного ти- па, резервируемых для данной переменной. Это поле является необя- зательным и определяет используемый динамический локальный массив (если он используется). Если данное поле пропущено, то резервиру- ется один элемент указанного типа. В итоге LocalArray состоит из 100 элементов размером в 1 байт, а LocalCount - из одного элемен- та размером в слово (см. пример).

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


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

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

Кстати, Borland C++ работает с границами стека так же, как мы здесь описали. Вы можете скомпилировать несколько модулей Borland C++ с параметром -S и посмотреть, какой код Ассемблера генерирует Borland C++ и как там создаются и используются границы стека.

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

Test(Flag, i, j, 1);

Тогда i находится по смещению 6, а не по смещению 4, j - по смещению 8, а не 6 и т.д. Для смещений параметров можно использо- вать директиву EQU:

. . . Flag EQU 4 AddParm1 EQU 6 AddParm2 EQU 8 SubParm1 EQU 10

mov ax[bp+AddParm1] add ax,[bp+AddParm1] sub ax,[bp+SubParm1] . . .

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


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

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

ARG FillArray:WORD, Count:WORD, FillValue:BYTE

Здесь задается три параметра: FillArray, параметр размером в слово, Count, также параметр размером в слово и FillValue - пара- метр размером в байт. Директива ARG устанавливает метку FillArray в значение [BP+4] (подразумевается, что код находится в процедуре ближнего типа), метку Count - в значение [BP+6], а метку FillValue - в значение [BP+8]. Однако особенно ценна дирек- тива ARG тем, что вы можете использовать определенные с ее по- мощью метки не заботясь о тех значениях, в которые они установле- ны.

Например, предположим, что у вас есть функция FillSub кото- рая вызывается из С++ следующим образом:

extern "C" { void FillSub( char *FillArray, int Count, char FillValue); }

main() { #define ARRAY_LENGTH 100 char TestArray[ARRAY_LENGTH]; FillSub(TestArray,ARRAY_LENGTH,'*'); }

В FillSub директиву ARG для работы с параметрами можно ис- пользовать следующим образом:

_FillSub PROC NEAR ARG FillArray:WORD, Count:WORD, FillValue:BYTE push bp ; сохранить указатель стека ; вызывающей программы mov bp,sp ; установить свой собственный ; указатель стека mov bx,[FillArray] ; получить указатель на ; заполняемый массив mov cx,[Count] ; получить заполняемую длину mov al,[FillValue] ; получить значение-заполнитель FillLoop: mov [bx],al ; заполнить символ inc bx ; ссылка на следующий символ loop FillLoop ; обработать следующий символ pop bp ; восстановить указатель стека ; вызывающей программы ret _FillSub ENDP

Не правда ли, удобно работать с параметрами с помощью дирек- тивы ARG? Кроме того, директива ARG автоматически учитывает раз- личные размеры возвратов ближнего и дальнего типа.

Переключение в режим Ideal и выход из него

Для переключения между режимом MASM и режимом Ideal можно использовать директивы IDEAL и MASM. Турбо Ассемблер всегда начи- нает ассемблирование исходного файла в режиме MASM. Для переклю- чения в режим Ideal перед использованием любых средств этого ре- жима в исходный файл нужно включить директиву IDEAL. С этого момента и до следующей директивы MASM все операторы будут вести себя так, как описывается в данной главе. Вы можете переключаться из режима MASM в режим Ideal в исходном файле столько раз, сколь- ко потребуется в любом месте исходного файла. Приведем пример:
DATA SEGMENT ; начало в режиме MASM abc LABEL BYTE ; abc адресуется к xyz, ; как к байту xyz DW 0 ; определить слово по ; метке xyz DATA ENDS ; завершить сегмент ; данных IDEAL ; перейти в ; режим Ideal SEGMENT CODE ; ключевое слово SEGMENT ; теперь следует первым PROC MyProc ; ключевое слово PROC ; тоже следует первым . . ; здесь можно программировать . ; в режиме Ideal END MyProc ; повторение метки MyProc ; необязательно ENDS ; повторение имени сегмента ; не требуется MASM ; переключение обратно в ; режим MASM CODE SEGMENT ; перед ключевым словом SEGMENT ; теперь требуется имя Func2 PROC ; имя теперь также следует перед ; ключевым словом PROC . . ; программирование в режиме . ; MASM IDEAL ; переключение обратно в . ; режим Ideal . ; программирование в . ; режиме Ideal MASM ; возвращение в режим MASM Func2 ENDP ; имя опять требуется указывать ; перед ключевым словом CODE ENDS ; здесь также требуется имя
Как вы можете заметить, в режиме Ideal ключевые слова дирек- тив (например, PROC или SEGMENT) указываются перед соответствую- щими именами идентификаторов, то есть в порядке, обратном тому, который принят в MASM. Кроме того имеется возможность повторить имя процедуры или сегмента после директив ENDP или ENDS. Добавле- ние этого имени идентифицирует сегмент или процедуру, которая за- канчивается, и делает программу более понятной. Это хороший прак- тический прием, особенно в программах, содержащих несколько вложенных процедур и сегментов. Однако указывать имя идентифика- тора после ENDP или ENDS необязательно.

Переменные операционной среды

В Турбо Ассемблере не используются переменные среды для оп- ределения параметров, используемых по умолчанию, что отвечает об- щему подходу фирмы Borland, используемому при создании программ- ных продуктов. Вместо этого параметры, используемые по умолчанию, следует помещать в файл конфигурации, и для различных проектов создавать соответствующие им файлы конфигурации.
Если при использовании MASM для конфигурирования нужным об- разом системы использовались переменные операционной среды INCLUDE или MASM, то для использования той же программы с Турбо Ассемблером нужно создать файл конфигурации. Все те параметры, которые были специфицированы, используя переменную среды MASM, нужно поместить непосредственно в файл конфигурации. Каталоги, указанные переменной INCLUDE, помещаются в файл конфигурации с помощью параметра /I командной строки.

Переопределение элементов таблицы

Если в одном и том же табличном типе вы описываете два или более элементов с одинаковым именем, то Турбо Ассемблер будет проверять их и анализировать, согласуются ли их размеры и типы. Если это не так, то будет генерироваться ошибка. Турбо Ассемблер будет использовать для элемента последнее начальное значение в таблице. Таким образом, вы можете переопределить начальное значе- ние таблицы поле того, как встроите ее в другую таблицу. Напри- мер:
FOO TABLE VIRTUAL MEM1:WORD=MEM1PROC, VIRTUAL MEM2:WORD=MEM2PROC FOO2 TABLE FOO, VIRTUAL MEM1:WORD=MEM3PROC ; переопределить ; наследуемый MEM1

Переопределение общей макрокоманды, состоящей из нескольких строк

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

Переопределение сегмента

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

Переопределение сегментной части адресного выражения

Адресные выражения имеют значения, состоящие из сегмента и смещения. Вы можете задавать сегмент явно в виде сегментного ре- гистра. (Если вы задаете его, как значение группы, Турбо Ассемб- лер определяет, какой сегментный регистр нужно использовать, на основе предполагаемых значение сегментных регистров (ASSUME)). Для изменения сегментной части адресного выражения ис- пользуется следующий синтаксис:
выражение_1 : выражение_2
Данная операция возвращает адресное выражение, использующее в качестве смещения выражение_2, а в качестве сегмента или значе- ния группы - выражение_1. Например:
VarPtr dd dgroup:memvar ; dgrout - это группа mov cl,es[si+4] ; переопределение сегмента : ; ES

Переопределяемые идентификаторы

Некоторые типы идентификаторов, которые поддерживает Турбо Ассемблер, рассматриваются как переопределяемые. Это означает, что вы можете в любом месте модуля переопределить идентификатор данного типа другим идентификатором данного типа. Этим свойством, например, обладают числовые идентификаторы:
foo = 1 mov ax,foo ; поместить 1 в AX foo = 2 mov ax,foo ; поместить в AX 2
В общем случае область действия данного переопределяемого идентификатора начинается в точке его определения и продолжается до того места, в котором он переопределяется. Область действия последнего переопределения идентификатора включает в себя область от начало модуля до первого определения идентификатора. Например:
mov ax,foo ; поместить 2 в AX! foo = 1 mov ax,foo ; поместить 1 в AX foo = 2 ; это определение расширяется на ; начало модуля. mov ax,foo ; поместить 2 в AX
Типы переопределяемых идентификаторов перечислены в следую- щем списке:
- текстовая макрокоманда;
- числовое выражение;
- макрокоманда из нескольких строк;
- структура/объединения;
- таблица;
- запись;
- перечисление.
Примечание: Об этом подробнее рассказывается в Главе 5.

Подавление корректировок

В режиме Ideal Турбо Ассемблер не генерирует сегментных кор- ректировок для частных сегментов (private), которые выравниваются на границу страницы или параграфа. Так как компоновщик не требует подобных корректировок, при ассемблировании программ в режиме Ideal может быть получен объектный код меньшего объема, компонов- ку которых компоновщик выполняет более быстро, чем компоновку объектных файлов, сгенерированных в режиме MASM. Далее показыва- ется, каким образом такие корректировки производятся в режиме MASM (но не в режиме IDEAL):
SEGMENT DATA PRIVATE PARA VAR1 DB 0 VAR2 DW 0 ENDS SEGMENT CODE ASSUME ds:DATA mov ax,VAR2 ; корректировок не требуется ENDS
Примечание: Это различие не влияет на код, который вы пишете. Здесь об этом упоминается только для вашего сведе- ния.

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

Если вы пишете на языке Си или С++, то все внешние метки должны начинаться с символа подчеркивания (_). Компилятор Си и С++ вставляет символы подчеркивания перед всеми именами внешних функций и переменных при их использовании в программе на Си/С++ автоматически, поэтому вам требуется вставить их самим только в ассемблерных кодах. Вы должны убедиться, что все ассемблерные об- ращения к функциям и переменным Си начинаются с символа подчерки- вания, и кроме того, вы должны вставить его перед именами всех ассемблерных функций и переменных, которые делаются общими и вы- зываются из программы на языке Си/С++.
Например, следующая программа на языке Си (link2asm.cpp):
extrn int ToggleFlag(); int Flag; main() { ToggleFlag(); }
правильно компонуется со следующей программой на Ассемблере (CASMLINK.ASM):
.MODEL SMALL .DATA EXTRN _Flag:word .CODE PUBLIC _ToggleFlag _ToggleFlag PROC cmp [_Flag],0 ; флаг сброшен? jz SetFlag ; да, установить его mov [_Flag],0 ; нет, сбросить его jmp short EndToggleFlag ; выполнено SetFlag: mov [_Flag],1 ; установить флаг EndToggleFlag: ret _ToggleFlag ENDP END
При использовании в директивах EXTERN и PUBLIC спецификатора языка Си правильно компонуется со следующей программой на Ассемб- лере (CSPEC.ASM):
.MODEL SMALL .DATA EXTRN C Flag:word .CODE PUBLIC C ToggleFlag ToggleFlag PROC cmp [Flag],0 ; флаг сброшен? jz SetFlag ; да, установить его mov [Flag],0 ; нет, сбросить его jmp short EndToggleFlag ; выполнено SetFlag: mov [Flag],1 ; установить флаг EndToggleFlag: ret ToggleFlag ENDP END
Примечание: Метки, на которые отсутствуют ссылки в программе не Си (такие, как SetFlag) не требуют предшеству- ющих символов подчеркивания.
Турбо Ассемблер автоматически при записи имен Flag и ToggleFlag в объектный файл поместит перед ними символ подчерки- вания.

Поддержка DPMI

Турбо Ассемблер поддерживает спецификацию Интерфейса защи- щенного режима DOS (DOS Protected Mode Interface - DPMI). Будучи разработанным комитетом ведущих компаний-производителей программ- ного и аппаратного обеспечения (включая Borland), DPMI определяет стандартный интерфейс, полностью использующий преимущества средств защищенного режима процессоров 80286, 386 и i486.
По мере увеличения числа компьютеров на базе процессоров 386 и i486 растет и число программных продуктов, использующих возмож- ности этих процессоров. Защищенный и виртуальный 8086 режимы дан- ных процессоров означают изменение самого вычислительного процес- са. Теперь мы можем использовать многозадачный режим и средства расширенной памяти. Раньше организация работы прикладной програм- мы, использующей расширенную память, в многозадачном режиме с другим программным обеспечением представляла собой проблему. Стандарт DPMI решает эту проблему. Прикладные программы, исполь- зующие расширенную память, разработанные в стандарте DPMI, будут надежно работать в многозадачных системах.
В настоящее время Турбо Ассемблер использует сервисные средства DPMI, предлагаемые Microsoft Windows. Турбо Ассемблер может работать в операционной среде, использующей средства DPMI. Если вы решите перейти на новую операционную среду, в которой эти сервисные средства доступны, то ваши затраты на программное обес- печение себя оправдают.
Примечание: Стандарт DPMI поддерживает версия Турбо Ас- семблера TASMX.EXE. Подробнее о TASMX.EXE рассказывается в Главе 2.

Подпрограмма шестнадцатиричного преобразования общего назначения

Содержащиеся в параметре num байты преобразуются в строку шестнадцатиричных цифр длины (byteCount * 2). Поскольку каждый байт порождает два символа, максимальное значение byteCount равно 127 (не проверяется). Для преобразования каждой группы (по 4 би- та) в шестнадцатиричную цифру мы для скорости используем последо- вательность add-daa-adc-daa.
Процедура HexStr (ее можно найти в файле HEX.ASM) написана так, что вызываться она должна с помощью вызова дальнего типа. Это означает, что ее следует описывать в интерфейсной части моду- ля Турбо Паскаля или с помощью директивы компилятора {$F+}.
CODE SEGMENT ASSUME cs:CODE,ds:NOTHING
; Параметры (+2 с учетом push bp)
byteCount equ byte ptr ss:[bp+6] num equ dword ptr ss:[bp+8]
; Адресация к результату функции (+2 с учетом push bp)
resultPtr equ dword ptr ss:[bp+12]
HexStr PROC FAR PUBLIC HexStr
push bp mov bp,sp ; получить указатель ; стека les di,resultPtr ; получить адрес ; результата функции mov dx,ds ; сохранить DS Турбо ; Паскаля в DX lds si,sum ; получить адрес числа mov al,byteCount ; сколько байт? xor ah,ah ; слово mov cx,ax ; отслеживать число ; байт в CX add si,ax ; начать со старшего ; байта числа dec si shl ax,1 ; сколько цифр? ; (2/байт) cld ; сохранить число цифр ; (работать в прямом ; направлении) stosb ; в приемнике - байт ; длины строки NextLoop: std ; сканировать число от ; старшего байта к ; младшему lodsb ; получить следующий ; байт mov ah,al ; сохранить его shr al,1 ; выделить старшую ; группу бит shr al,1 shr al,1 shr al,1 add al,90h ; специальная после- ; довательность шестнад- ; тиричного преобразования daa ; использование инструкций ; ADD и DAA adc al,40h daa ; группа преобразована ; в код ASCII cld ; сохраним ASCII и следуем ; далее stosb mov al,ah ; повторить преобразование ; для младшей группы and al,0Fh add al,90h daa adc al,40h daa stosb loop HexLoop ; продолжать, пока не ; будет выполнено mov ds,dx pop bp ret 6 ; параметры занимают ; 6 байт HexStr ENDP CODE ENDS END
Пример программы на Паскале, где используется функция HexStr, имеет следующий вид:

Program HexTest; var num : word;

{$F+}

function HexStr(var num; byteCount : byte) : string; external;

{$L HEXSTR.OBJ}

{$F-} begin num := word; Writeln(' Преобразованная строка имеет шестнадцатиричное представление: ', HexStr(num,Sizeof(num)),'*'); end.

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

TASM HEXSTR TPC HEXTEST HEXTEST

Если вы используете директиву .MODEL, то программу HexStr можно записать следующим образом (файл HEXMOD.ASM):

.MODEL large, PASCAL .CODE HexStr PROC FAR num:DWORD,byteCount:BYTE RETURNS resultPtr:DWORD PUBLIC HexStr les di,resultPtr ; получить адрес ; результата функции mov dx,ds ; сохранить DS Турбо ; Паскаля в DX lds si,sum ; получить адрес числа mov al,byteCount ; сколько байт? xor ah,ah ; слово mov cx,ax ; отслеживать число ; байт в CX add si,ax ; начать со старшего ; байта числа dec si shl ax,1 ; сколько цифр? ; (2/байт) cld ; сохранить число цифр ; (работать в прямом ; направлении) stosb ; в приемнике - байт ; длины строки NextLoop: std ; сканировать число от ; старшего байта к ; младшему lodsb ; получить следующий ; байт mov ah,al ; сохранить его shr al,1 ; выделить старшую ; группу бит shr al,1 shr al,1 shr al,1 add al,90h ; специальная после- ; довательность шестнад- ; тиричного преобразования daa ; использование инструкций ; ADD и DAA adc al,40h daa ; группа преобразована ; в код ASCII cld ; сохраним ASCII и следуем ; далее stosb mov al,ah ; повторить преобразование ; для младшей группы and al,0Fh add al,90h daa adc al,40h daa stosb loop HexLoop ; продолжать, пока не ; будет выполнено mov ds,dx ; восстановить DS ; Турбо Паскаля ret HexStr ENDP CODE ENDS END

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

Подразумеваемое сложение

В режиме MASM сложение выражений можно выполнять несколькими способами: с помощью операции сложения (+), с помощью операции точки (.), или с помощью подразумеваемого (неявного) сложения (когда выражения разделяются квадратными и круглыми скобками). Например:
MOV AX,5[BX] ; содержимое по адресу BX+5 MOV AX,5[XYZ] ; содержимое по адресу XYZ+5
Неявная операция сложения имеет следующий общий синтаксис:
выражение_1 [выражение_2] или выражение_1 (выражение_2)

Pointer expression needs brackets

(Адресное выражение должно быть заключено в квадратные скоб- ки)
В режиме Ideal операнд, содержащий имя идентификатора, представляющее собой ссылку на память, не заключен в квадратные скобки. В режиме Ideal квадратные скобки означают ссылку на адрес памяти. Например:
B DB 0 mov al,B ; предупреждение: в режиме Ideal ; должно быть указано [B]
Т.к. в режиме MASM квадратные скобки не обязательны, то это сообщение выдается как предупреждающее.

Получение сегмента и смещения адресного выражения

Для получения сегмента и смещения адресного выражения вы мо- жете использовать операции SEG и OFFSET. Операция SEG возвращает сегментную часть адресного выражения. Она имеет следующий син- таксис:
SEG выражение
Приведем пример исходного кода:
DATASEG temp DW 0 CODESEG mov ax,SEG temp mov ds,ax ASSUME ds:SEG temp
Операция OFFSET возвращает смещение адресного выражения и имеет следующий синтаксис:
OFFSET выражение
Заметим, что когда вы используете операцию смещения, убеди- тесь, что выражение ссылается на корректный сегмент. Например, если вы используете режим MASM и не используете упрощенные дирек- тивы, выражение:
OFFSET BUFFER ; буфер - это адрес в памяти
что не тоже самое, что:
OFFSET DGROUP:BUFFER ; Dgroup - это группа, содержащая ; сегмент, который содержит BUFFER
(если содержащий BUFFER сегмент не является первым сегментом группы).
Если вы не переопределяете сегмент с помощью операции :, в режиме Ideal адреса автоматически вычисляются относительно любой группы, которой принадлежит сегмент. В режиме MASM это имеет мес- то при использовании упрощенных сегментных директив. В противном случае адреса вычисляются относительно сегмента, в котором нахо- дится объект, а не какой-либо группы.

Получение типа выражения

В режиме MASM вы можете получить числовое значение типа вы- ражения путем использования операции TYPE. (В режиме MASM этого сделать нельзя, поскольку типы не описываются числовыми значения- ми.) Операция TYPE имеет следующий синтаксис:
TYPE выражение
Операция TYPE возвращает размер объекта, описанный адресным выражением:
Значение TYPE Таблица 5.20 ---------------------------T------------------------------------¬ ¦ Выражение ¦ Значение ¦ +--------------------------+------------------------------------+ ¦ byte ¦ 1 ¦ ¦ word ¦ 2 ¦ ¦ dword ¦ 4 ¦ ¦ pword ¦ 6 ¦ ¦ qword ¦ 8 ¦ ¦ tbyte ¦ 10 ¦ ¦ short ¦ 0ffffh ¦ ¦ near ¦ 0ffffh ¦ ¦ far ¦ 0fffeh ¦ ¦ структура/объединение ¦ Размер экземпляра структуры или ¦ ¦ ¦ объединения. ¦ ¦ таблица ¦ Размер экземпляра таблицы. ¦ L--------------------------+-------------------------------------
Приведем пример:
avar = 5 darray dd 10 dup (1) x struc dw ? dt ? ends fp label far tavar = TYPE avar ; = 0 tbvar = TYPE davar ; = 4 tx = TYPE x ; = 12 tfp = TYPE fp ; = 0FFFFh

Получение значения старшего или младшего байта выражения

Для возврата значения старшего или младшего байта выражения можно использовать операции HIGH и LOW. Это обстоятельство может быть полезно, например, в ситуации, когда требуются только стар- шие 8 бит смещения адреса.
Приведем пример операций HIGH и LOW:
HIGH выражение LOW выражение
Например:
magic equ 1234h mov cl,HIGH magic ; cl = 12h mov cl,LOW magic ; cl = 34h

Поразрядные операции сдвига

Поразрядные операции сдвига смещают значения вправо или влево на фиксированное число бит. Вы можете использовать их для быстрого выполнения операций умножения или деления или для досту- па к битовому полю в значении. В следующей таблице приведен спи- сок операций сдвига.
Логические операции сдвига Таблица 5.17 ------------------------------T---------------------------------¬ ¦ Выражение ¦ Значение ¦ +-----------------------------+---------------------------------+ ¦ выражение_1 SHL выражение_2 ¦ Выражение_1 сдвигается влево на ¦ ¦ ¦ число бит, заданных выражением_ ¦ ¦ ¦ 2 (при отрицательном значении ¦ ¦ ¦ выражения_2 выполняется сдвиг ¦ ¦ ¦ вправо). ¦ ¦ ¦ ¦ ¦ выражение_1 SHR выражение_2 ¦ Выражение_1 сдвигается вправо ¦ ¦ ¦ на число бит, заданных выраже- ¦ ¦ ¦ нием_2 (при отрицательном зна- ¦ ¦ ¦ чении выражения_2 выполняется ¦ ¦ ¦ сдвиг влево). ¦ L-----------------------------+----------------------------------

Positive count expecting

(Счетчик должен быть положительным)
В выражении для DUP в качестве счетчика повторений указано отрицательное число. Например:
BUF -1 DUP (?) ; ошибка: отрицательный счетчик
Счетчик в операции DUP должен быть равен или больше 1.

Предопределенные идентификаторы

Два предопределенных идентификатора @Cpu и @WordSize позволяют вам получить информацию о типе используемого процессора или размере текущего сегмента. Приведем описание этих идентифика- торов.

@curseg @fardata

У идентификаторов, начинающихся с двух вопросительных знаков (??), все буквы должны быть в нижнем регистре, например:

??date ??version

Заметим, что идентификатор ?? date определяет текстовое прис- ваивание, которое представляет текущую дату. Точный формат строки даты определяется кодом страны, установленным в DOS. Идентифика- тор ??version позволяет вам писать исходные файлы, использующие средства различных версий Турбо Ассемблера. Это присваивание поз- воляет вам также определить, ассемблируется ли исходный файл с помощью MASM или с помощью Турбо Ассемблера, поскольку идентифи- катор ??version в MASM не определен. Аналогично, ??filename опре- деляет строку из 8 символов, представляющую собой имя текущего ассемблируемого файла. Если имя файла содержит менее 8 символов, то оно дополняется пробелами. Идентификатор ??time определяет текстовое присваивание, которое представляет текущее время. Точ- ный формат строки времени определяется кодом страны, установлен- ным в DOS.

Предопределенные идентификаторы

В Турбо Ассемблере предусмотрено несколько предопределенных идентификаторов, которые вы можете использовать в программах. В разных местах этого кода эти идентификаторы могут иметь различное значение. Они аналогичны строковым присваиваниям, выполняемым по директиве EQU. Когда Турбо Ассемблер обнаруживает в исходном фай- ле один из этих идентификаторов, он заменяет его текущим значени- ем данного предопределенного идентификатора.
Некоторые из этих идентификаторов представляют собой тексто- вые (строковые) присваивания, другие - числовые присваивания, а третьи являются псевдонимами. Строковые значения могут использо- ваться в любом месте, где допускается использование текстовых строк, например, для инициализации последовательности байтовых данных с помощью директивы DB:
NOW DB ??time
Числовые предопределенные значения можно использовать в лю- бом месте, где допустимо использование числа:
IF ??version GT 100h
Значения-псевдонимы превращают предопределенные идентифика- торы в синоним представляемого значения, что позволяет использо- вать имя предопределенного идентификатора в любом месте, где можно использовать обычное имя идентификатора:
ASSUME cs:@code
Все предопределенные идентификаторы могут использоваться как в режиме MASM, так и в режиме Ideal.
Если вы при ассемблировании используете параметр командной строки /ml, то предопределенные идентификаторы нужно использовать в точности так, как они описаны ниже (соблюдая регистр символов).
К идентификаторам, начинающимся с символа @, применимо сле- дующее правило: первая буква каждого слова, которое составляет имя идентификатора, указывается в верхнем регистре (кроме имен сегментов), а остальная часть слова - в нижнем. Например:
@FileName
Заметим, что @FileName представляет собой присваивание псевдонима для текущей ассемблируемой строки.
Исключением являются переопределенные идентификаторы, кото- рые ссылаются на сегменты. Имена сегментов начинаются с символа @ и записываются символами в нижнем регистре. Например:

Предупреждающие сообщения и сообщения об ошибках

Предупреждающие сообщения выдаются для информирования поль- зователей о возможных нежелательных последствиях ассемблирования оператора исходного файла. Предупреждающее сообщение выдается, например, в тех случаях, когда использование Turbo Assembler ка- кого-либо, вообще говоря, допустимого умолчания может привести к некорректным результатам. Рекомендуется обязательно проанализиро- вать причины предупреждающих сообщений, чтобы не допустить гене- рации неверного кода. Наличие этих сообщений не влияет на генера- цию объектного кода Turbo Assembler. Предупреждающие сообщения имеют следующий формат:
**Warning** имя_файла(номер_строки) сообщение
Если предупреждающее сообщение выдается во время расширения макрокоманды или блока повторения, то оно будет содержать допол- нительную информацию: имя макрокоманды и номер строки, при ас- семблировании которой возникло это сообщение. Предупреждающее со- общение в этом случае имеет следующий формат:
**Warning** имя_файла(номер_строки) имя_макрокоманды(номер_строки_в_макрокоманде)сообщение
В отличие от предупреждающих сообщений, появление сообщений об ошибках означает, что объектный код Турбо Ассемблер генериро- вать не будет, хотя процесс ассемблирования будет доведен до кон- ца. Типичное сообщение об ошибке имеет следующий формат:
**Error** имя_файла(номер_строки) сообщение
Если сообщение об ошибке выдается во время расширения макро- са или блока повтора, то оно будет содержать дополнительную ин- формацию: имя макрокоманды и номер строки, при ассемблировании которой возникло это сообщение. Сообщение об ошибке в этом случае имеет следующий формат:
**Error** имя_файла(номер_строки) имя_макрокоманды(номер_строки_в_макрокоманде)сообщение
Далее в алфавитном порядке приводятся тексты предупредитель- ных сообщений и сообщений об ошибках:

A. Замечания по программированию

В данном приложении приведена основная информация по постро- ению программ с конкретными моделями памяти и форматами выполняе- мого кода.

B. Обзор синтаксических правил Турбо Ассемблера

В этом приложении для описания синтаксиса выражений Турбо Ассемблера, используется модифицированная форма Бекуса-Наура (BNF). Символ ::= описывает синтаксический результат. Далее во всех разделах многоточие (.) означает, что предшествующий эле- мент может быть повторен любое число раз. В данном приложении описываются также ключевые слова и их старшинство.

C. Вопросы совместимости

Турбо Ассемблер в режиме MASM в высокой степени совместим с ассемблером MASM версии 5.2. Однако стопроцентная совместимость - это идеал, к которому можно только стремиться, т.к. формальная спецификация языка отсутствует, и даже различные версии MASM не полностью совместимы друг с другом.
Для большинства программ замена MASM на Турбо Ассемблер не повлечет за собой каких-либо осложнений. В некоторых случаях в Турбо Ассемблере появятся предупреждающие сообщения и сообщения об ошибках там, где их в MASM не было, что обычно означает, что ошибочный оператор не был выявлен макроассемблером MASM. Напри- мер, MASM не обнаруживает ошибок в следующих операторах:
abc EQU [BP+2] PUBLIC abc
и генерирует бессмысленный объектный код. В Турбо Ассемблере по- добная конструкция, а также ряд других двусмысленных конструкций, будут идентифицированы как ошибочные.
Если при ассемблировании программы пользователя с помощью Турбо Ассемблера возникнут какие-либо затруднения, то можно попы- таться использовать директиву QUIRKS. Например, если с помощью следующей командной строки:
TASM /JQUIRKS MYFILE
программа будет ассемблирована правильно, то следует добавить в начало исходного файла директиву QUIRKS. Еще лучше, используя сведения из этого приложения, определить операторы, для которых требуется директива QUIRKS, и после этого переписать соответству- ющие строки исходного файла так, чтобы отпала необходимость ис- пользования директивы QUIRKS.
Для максимальной совместимости с MASM следует использовать директиву NOSMART и режим QUIRKS.

D. Утилиты Турбо Ассемблера

Турбо Ассемблер TASM предоставляет вам семь мощных автоном- ных утилит. Вы можете использовать их для файлов TASM, а также для любых других файлов.
Это такие утилиты, как:
¦ MAKE (включая утилиту TOUCH; автономный менеджер прог- рамм);
¦ TLINK (компоновщик);
¦ TLIB (библиотекарь);
¦ TASMHELP (оперативный справочник);
¦ GREP (утилита поиска текста в файлах);
¦ OBJXREF (утилита для работы с перекрестными ссылками в объектных модулях);
¦ TCREF (утилита для работы с перекрестными ссылками);
¦ H2ASH (утилита-преобразователь файлов .h в файл .ash).
Подробности об использовании утилит MAKE, TOUCH, TLINK, GREP, OBJXREF, TASMHELP, H2ASH можно узнать в документации по компилятору. TASMHELP работает аналогично утилите THELP, описание которой также можно найти в документации по компилятору. О том, как использовать утилиту TCREF в текстовых файлах, можно узнать файлах документации на дисках Турбо Ассемблера. Утилита H2ASH также описывается в файлах на дисках Турбо Ассемблера, но пос- кольку это новая утилита, мы подробнее коснемся ее в данном при- ложении.

E. Сообщения об ошибках

В данном приложении описаны все сообщения, генерируемые Тур- бо Ассемблером. Обычно сообщения выдаются на экран, однако их можно переадресовать в файл или на принтер с помощью стандартных средств DOS переадресации потоков, т.е. надо указать имя устройс- тва или файла с предшествующим ему символом "больше чем" (>). Например:
TASM MYFILE > ERRORS
В Турбо Ассемблере генерируются сообщения следующих типов:
- информационные сообщения; - предупреждающие сообщения; - сообщения об ошибках; - сообщения о фатальных ошибках.

Пример анализа операционной среды DOS

С помощью функции EnvString вы сможете просмотреть операци- онную среду DOS и найти строку вида "s=НЕЧТО" и возвратить НЕЧТО, если это найдено.
DATA SEGMENT PUBLIC EXTRN prefixSeg : Word ; дает адрес PSP DATA ENDS SEGMENT PUBLIC ASSUME cs:CODE,ds:DATA
EnvString PROC FAR PUBLIC EnvString push bp cld ; работать в прямом ; направлении mov es,[prefixSeg] ; посмотреть PSP mov es,es:[2Ch] ; ES:DI указывают на ; операционную среду, xor di,di ; которая выровнена на ; границу параграфа mov bp,sp ; найти строку параметров, lds si,ss:[bp+6] ; которая следует за ; адресом возврата ASSUME ds:NOTHING lodsb ; посмотреть длину or al,al ; она равна 0? jz RetNul ; да, возврат mov ah,al ; в противном случае ; сохранить ее в AH mov dx,si ; DS:SI содержат указатель ; на первый параметр ; char xor al,al ; сделать его равным 0 Compare: mov ch,al ; мы хотим, чтобы для ; следующего отсчета ch=0 mov si,dx ; возвратить указатель на ; просмотренную строку mov cl,ah ; получить длину mov si,dx ; возвратить указатель на ; строку repe cmpsb ; сравнить байты jne Skip ; если сравнение неудач- ; ное попробовать следу- ; ющую строку cmp byte ptr es:[di],'=' ; сравнение ; завершилось успешно ; следующий символ '='? jne NoEqual ; если нет, все еще нет ; совпадения Found: mov ax,es ; DI:SI будет указывать ; на найденную нами строку mov ds,ax mov si,di inc si ; "пройти" символ '=' les bx,ss:[bp+10] ; получить адрес ; результата ; функции mov di,bx ; занести его в ES:DI inc di ; байт длины mov cl,255 ; задать максимальную ; длину CopyLoop: lodsb ; получить байт or al,al ; проверить на 0 jz Done ; если 0, выполнено stosb ; занести его в результат loop CopyLoop ; переместить до 255 ; байт Done: not cl ; при сохранении мы ; уменьшали от CL до 255 mov es:[bx],cl ; сохранить длину mov ax,SEG DATE mov ds,ax ; восстановить DS ASSUME ds:DATA pop bp ret 4 ASSUME ds:NOTHING Skip: dec di ; проверить на 0 NoEqual: mov cx,7FFFh ; длинный поиск, если ; нужно sub cx,di ; операционная среда ; никогда не превышает ; 32К jbe RetNul ; если конец, выйти repne scasb ; посмотреть следующий ; 0 jcxz RetNul ; выйти, если не найден cmp byte ptr es:[di],al ; второй 0 в строке? jne Compare ; если нет, попытаться ; снова RetNul: les di,ss:[bp+10] ; получить адрес ; результата stosb ; сохранить там 0 mov ax,SEG DATA mov ds,ax ; восстановить DS ASSUME ds:DATA pop bp ret 4 EnvString ENDP CODE ENDS END

Программа на Паскале, которая использует функцию EnvString, выглядит следующим образом:
program EnvTest; { программа ищет строки операционной среды }
var EnvVariable : string; EnvValue : string;
{$F+}
function EnvString(s:string) : string; external; {$L ENVSTRING.OBJ} {$F-} begin EnvVariable := 'PROMPT'; EnvValue := EnvString(EnvVariable); if EnvValue = '' then EnvValue := '*** не найдена ***'; Writeln('Переменная операционной среды: ', EnvVariable,' Значение: ',EnvValue); end.
Чтобы сформировать и запустить данные программы на Паскале и Ассемблере, используйте следующие команды командного файла:
TASM ENVSTR TPC ENVTEST ENVTEST
Если использовать директиву .MODEL, то функция EnvString на Ассемблере будет выглядеть следующим образом (ENVMOD.ASM):
.MODEL large, PASCAL .DATA EXTRN prefixSeg : Word ; дает адрес PSP .CODE EnvString PROC FAR EnvVar:DWORD RETURNS EnvVal:DWORD PUBLIC EnvString push bp cld ; работать в прямом ; направлении mov es,[prefixSeg] ; посмотреть PSP mov es,es:[2Ch] ; ES:DI указывают на ; операционную среду, xor di,di ; которая выровнена на ; границу параграфа mov bp,sp ; найти строку параметров, lds si,ss:[bp+6] ; которая следует за ; адресом возврата ASSUME ds:NOTHING lodsb ; посмотреть длину or al,al ; она равна 0? jz RetNul ; да, возврат mov ah,al ; в противном случае ; сохранить ее в AH mov dx,si ; DS:SI содержат указатель ; на первый параметр ; char xor al,al ; сделать его равным 0 Compare: mov ch,al ; мы хотим, чтобы для ; следующего отсчета ch=0 mov si,dx ; возвратить указатель на ; просмотренную строку mov cl,ah ; получить длину mov si,dx ; возвратить указатель на ; строку repe cmpsb ; сравнить байты jne Skip ; если сравнение неудач- ; ное, попробовать следу- ; ющую строку cmp byte ptr es:[di],'=' ; сравнение ; завершилось успешно ; следующий символ '='? jne NoEqual ; если нет, все еще нет ; совпадения Found: mov ax,es ; DI:SI будет указывать ; на найденную нами строку mov ds,ax mov si,di inc si ; "пройти" символ '=' les bx,ss:[bp+10] ; получить адрес ; результата функции mov di,bx ; занести его в ES:DI inc di ; байт длины mov cl,255 ; задать максимальную ; длину CopyLoop: lodsb ; получить байт or al,al ; проверить на 0 jz Done ; если 0, выполнено stosb ; занести его в результат loop CopyLoop ; переместить до 255 ; байт Done: not cl ; при сохранении мы ; уменьшали от CL до 255 mov es:[bx],cl ; сохранить длину mov ax,SEG DATE mov ds,ax ; восстановить DS ASSUME ds:DATA pop bp ret 4 ASSUME ds:NOTHING Skip: dec di ; проверять на 0 NoEqual: mov cx,7FFFh ; длинный поиск, если ; нужно sub cx,di ; операционная среда ; никогда не превышает ; 32К jbe RetNul ; если конец, выйти repne scasb ; посмотреть следующий ; 0 jcxz RetNul ; выйти, если не найден cmp byte ptr es:[di],al ; второй 0 в строке? jne Compare ; если нет, попытаться ; снова RetNul: les di,ss:[bp+10] ; получить адрес ; результата stosb ; сохранить там 0 mov ax,SEG DATA mov ds,ax ; восстановить DS ASSUME ds:DATA ret 4 EnvString ENDP CODE ENDS END
Вы можете использовать ту же программу на Паскале и просто ассемблировать альтернативный вариант функции EnvString и пере- компилировать программу с помощью того же командного файла.
Далее следует содержимое приложений.
Назад | Содержание | Вперед

Пример из области программирования

На диске с примерами содержится исчерпывающий пример объект- но-ориентированного программирования, в котором используются опи- санные ранее объекты list и queue, а также объект stack. Описан- ный объект node является базовым объектом для всех данных пользователя, записанных в связанном списке, очереди или стеке. Список соответствующих файлов примера приведен в следующей табли- це:
Файлы примера объектно-ориентированного программирования
Таблица 4.3 -----------------T----------------------------------------------¬ ¦ Файл ¦ Содержимое ¦ +----------------+----------------------------------------------+ ¦ NODE.ASO ¦ Описывает объект node и методы. ¦ ¦ NODE.ASM ¦ Содержит методы объекта node и экземпляр таб-¦ ¦ ¦ лицы виртуальных методов. ¦ ¦ ¦ ¦ ¦ LIST.ASO ¦ Описывает объект list и его методы. ¦ ¦ LIST.ASM ¦ Содержит методы объекта list и экземпляр таб-¦ ¦ ¦ лицы виртуальных методов. ¦ ¦ ¦ ¦ ¦ QUEUE.ASO ¦ Описывает объект queue и его методы. ¦ ¦ QUEUE.ASM ¦ Содержит методы объекта queue и экземпляр¦ ¦ ¦ таблицы виртуальных методов. ¦ ¦ ¦ ¦ ¦ STACK.ASO ¦ Описывает объект stack и его методы. ¦ ¦ STACK.ASM ¦ Содержит методы объекта stack и экземпляр¦ ¦ ¦ таблицы виртуальных методов. ¦ ¦ ¦ ¦ ¦ OOP.ASM ¦ Содержит пример использования этих объектов. ¦ L----------------+-----------------------------------------------
Назад | Содержание | Вперед

Пример объекта

В качестве примера использования объектов рассмотрим прог- рамму, работающую со связанными списками. Мы будем рассматривать связанных список как объект, содержащий связанный список данных и операций (методов), которые вы можете с ними выполнять.
Связанный список данных состоит из указателей на начало ("голову") и конец ("хвост") связанного списка (в нашем примере из-за его гибкости используется двунаправленный связанный спи- сок). Каждый элемент связанного списка представляет собой реали- зацию отдельного объекта.
Возможности, необходимые для использования связанного спис- ка, предоставляют следующие операции:
- создание связанного списка (выделение для него памяти); - уничтожение связанного списка (освобождение используемой памяти); - инициализация связанного списка; - деинициализация связанного списка; - вставка элемента в середину списка перед существующим эле- ментом; - присоединение элемента к концу связанного списка; - удаление элемента из связанного списка; - возвращение первого элемента связанного списка; - возвращение последнего элемента связанного списка.
Имейте в виду, что создание и инициализация, а также уничто- жение и деинициализация методов - это не синонимы. При создании и уничтожении методы create и destroy выделяют и освобождают память для объекта (связанного списка), а методы инициализации и деини- циализации initialize и deinitialize только инициализируют и деи- нициализируют ранее выделенные экземпляры объекта.
Вы можете видеть, как объект связанного списка наследуется объектами стека или очереди, поскольку очередь и стек можно реа- лизовать как связанный список с ограниченным числом операций. Например, можно реализовать очередь в виде связанного списка, в котором элементы могут добавляться к концу и извлекаться из нача- ла. Если вы таким образом реализуете очередь, то нужно запретить наследуемые методы связанного списка, которые для очереди недо- пустимы (например, вставку в середину списка).

Пример обмена содержимого двух переменных

С помощью данной процедуры (VAREXCH.ASM) вы можете выполнить обмен содержимого двух переменных размера count. Если count имеет значение 0, то то процессор попытается перекопировать 64К.
CODE SEGMENT ASSUME cs:CODE,ds:NOTHING
; Параметры (заметим, что из-за push bp смещение ; увеличивается на 2)
var1 equ DWORD PTR ss:[bp+12] var2 equ DWORD PTR ss:[bp+8] count equ WORD PTR ss:[bp+6]
Exchange PROC FAR PUBLIC Exchange cld ; обмен в прямом направлении mov dx,ds ; сохранить регистр DS push bp mov bp,sp ; получить базу стека lds si,var1 ; получить первый адрес les di,var2 ; получить второй адрес mov cx,count ; получить число перемещаемых ; байт shr cx,1 ; получить счетчик слов ; (младший бит -> перенос) jnc ExchangeWord ; если не нечетный байт, ; войти в цикл mov al,es:[di] ; считать нечетный байт ; из var2 movsb ; переместить байт из var1 ; в var2 mov [si-1],al ; записать var2 в var1 jz Finis ; выполнено, если нужно ; выполнить обмен только ; одного байта ExchangeWords: mov bx,-2 ; BX - это удобное место ; для хранения -2 ExchangeLoop: mov ax,es:[di] ; считать слово из var2 movsw ; переместить из var1 ; в var2 mov [bx][si,ax ; записать слово var2 в ; var1 loop ExchangeLoop ; повторить count/2 раз Finis: mov ds,dx ; получить обратно DS ; Турбо Паскаля pop bp ret 10 Exchange ENDP CODE ENDS END
Программа Турбо Паскаля, которая использует функцию Exchange (файл varexch.pas), имеет вид:
program TextExchange;
type EmployeeRecord = record Name : string[30]; Address : string[30]; City : string[15]; State : string[2]; Zip : string[10]; end;
var OldEmployee, NewEmployee : EmployeeRecord;
{$F+}
procedure Exchange(var var1,var2; count : word); external; {$L XCHANGE.OBJ} {$F-} begin with OldEmployee do begin Name := 'John Smith'; Address := ' 123 F Street'; City := 'Scotts Valley'; State := 'CA'; Zip := ' 90000-0000'; end; with NewEmployee do begin Name := 'Mary Jones'; Address := ' 9471 41st Avenue'; City := 'New York'; State := 'NY'; Zip := ' 10000-1111'; end; Writeln('Before: ',OldEmployee.Name,' ',NewEmployee.Name); Exchange(OldEmployee,NewEmployee,sizeof(OldEmployee)); Writeln('After: ',OldEmployeeName,' ',NewEmployee.Name); Exchange(OldEmployee,NewEmployee,sizeof(OldEmployee)); Writeln('After: ',OldEmployeeName,' ',NewEmployee.Name); end.

Чтобы сформировать и запустить данные программы на Паскале и Ассемблере, используйте следующие команды командного файла:
TASM XCHANGE TPC XCHANGE XCHANGE
Если использовать директиву .MODEL, то программа Exchange на Ассемблере будет выглядеть следующим образом:
.MODEL large, PASCAL .CODE Exchange PROC FAR var1:DWORD,var2:DWORD,count:WORD PUBLIC Exchange cld ; обмен в прямом направлении mov dx,ds ; сохранить DS push bp mov bp,sp ; получить базу стека lds si,var1 ; получить первый адрес les di,var2 ; получить второй адрес mov cx,count ; получить число перемещаемых ; байт shr cx,1 ; получить счетчик слов ; (младший бит -> перенос) jnc ExchangeWord ; если не нечетный байт, ; войти в цикл mov al,es:[di] ; считать нечетный байт ; из var2 movsb ; переместить байт из var1 ; в var2 mov [si-1],al ; записать var2 в var1 jz Finis ; выполнено, если нужно ; выполнить обмен только ; одного байта ExchangeWords: mov bx,-2 ; BX - это удобное место ; для хранения -2 ExchangeLoop: mov ax,es:[di] ; считать слово из var2 movsw ; переместить из var1 ; в var2 mov [bx][si,ax ; записать слово var2 в ; var1 loop ExchangeLoop ; повторить count/2 раз Finis: mov ds,dx ; получить обратно DS ; Турбо Паскаля ret Exchage ENDP CODE ENDS END
Вы можете использовать ту же программу на Паскале и просто ассемблировать альтернативный вариант процедуры Exchаnge и пере- компилировать программу с помощью того же командного файла.

Примеры макрокоманд работы с текстом

Следующие примеры показывают, как работают эти операции:
VERSION T300 ABC EQU ; ABC = "abc" ABC2 EQU ABC ; ABC2 = "ABC" ABC EQU ; ABC = "det" ; (переопределяется) ABC3 CATSTR ABC2,<,>,ABC,<,>,ABC2 ;ABC3 = "ABC,DEF, ; ABC" ABCLEN SIZESTR ABC ; ABCLEN = 3 ABC3LEN SIZESTR ABC3 ; ABC3LEN = 11 COMMA1 INSTR ABC3,<,> ; COMMA1 = 4 COMMA2 INSTR COMMA1+1,ABC3,<,> ; COMMA2 = 8 ABC4 SUBSTR ABC3,5 ; ABC4 = "def,ABC" ABC5 SUBSTR ABC3,5,3 ; ABC5 = "def" ABC6 EQU 3+2+1 ; ABC6 = 6 ; (числовое ; присваивание) ABC7 EQU %3+2+1 ; ABC7 = "6" ; (текстовая ; макрокоманда) ABC8 EQU %COMMA1 ; ABC8 = "4"

Примеры подпрограмм на Ассемблере для Турбо Паскаля

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

Принудительное переопределение сегментов: инструкции SEGxx

В Турбо Ассемблере предусмотрены 6 инструкций, которые приводят к генерации переопределений сегмента. Эти инструкции пе- речислены в следующей таблице:
Инструкции переопределения сегмента Таблица 13.3 ----------------T-----------------------------------------------¬ ¦ Инструкция ¦ Значение ¦ +---------------+-----------------------------------------------+ ¦ SEGCS ¦ Генерирует префиксный байт переопределения CS.¦ ¦ ¦ ¦ ¦ SEGSS ¦ Генерирует префиксный байт переопределения SS.¦ ¦ ¦ ¦ ¦ SEGDS ¦ Генерирует префиксный байт переопределения DS.¦ ¦ ¦ ¦ ¦ SEGES ¦ Генерирует префиксный байт переопределения ES.¦ ¦ ¦ ¦ ¦ SEGFS ¦ Генерирует префиксный байт переопределения FS.¦ ¦ ¦ ¦ ¦ SEGGS ¦ Генерирует префиксный байт переопределения GS.¦ L---------------+------------------------------------------------
Вы можете использовать эти инструкции в сочетании с такими инструкциями, как XLATB, которые не требуют аргументов, но могут использовать переопределение сегментов, например:
SEGCS XLATB
Заметим, что в большинстве инструкций имеется альтернативная форма, при которой, чтобы указать на необходимость переопределе- ния, вы можете задать пустой аргумент:
XLAT BYTE cs:[bx]
Приведенные два примера генерируют один и тот же код.

Присваивание идентификаторам значений

Турбо Ассемблер предоставляет две директивы, которые позво- ляют присвоить идентификатору значение: EQU и =. Директива EQU определяет строковое присваивание, присваивание псевдонима или числовое присваивание. Она имеет следующий синтаксис:
имя EQU выражение
где "имени" присваивается результат вычисления выражения. "Имя" должно быть новым идентификатором, который ранее подобным образом не определялся. Если первое определение представляло собой стро- ковое присваивание, в режиме MASM вы можете только переопреде- лить идентификатор, определенный по директиве EQU. В режиме MASM EQU может генерировать любой из трех видов присваиваний: строко- вое, числовое или присваивание псевдонима.
Директива = определяет только числовое присваивание. Она имеет синтаксис:
имя = выражение
где "имени" присваивается результат вычисления выражения, при вы- числении которого должна получаться константа или адрес в сегмен- те. "Имя" может быть новым идентификатором или идентификатором, уже определенным ранее по директиве =. Поскольку директива = име- ет намного более предсказуемое поведение, чем директива EQU в ре- жиме MASM, по возможности используйте директиву EQU.

Продолжение строки

Для тех строк, длина которых превышает 80 символов, в Турбо Ассемблере предусмотрен символ продолжения \. Поскольку Турбо Ас- семблер игнорирует любой символ, следующий за \ на данной строке, используйте этот символ в конце строки.
При использовании символа \ максимальная длина строки сос- тавляет 1024 символа. Однако таблицы, записи и перечисления могут содержать определения, превышающие 1024 символа. В качестве аль- тернативы, при которой не накладывается ограничение в 1024 сим- вола, можно использовать синтаксис с множественными определения- ми. Приведем пример такого синтаксиса (для определения enum):
foo enum ( ;Множественная версия f1 f2 f3 f4 f5 f6 f7 f8 )
Можно дать более компактную версию этого определения:
foo enum f1,f2( ;Компактная версия f3,f4 f5,f6 f7,f8)
При использовании множественных определений нужно соблюдать следующие правила:
- левая скобка, которая начинает определение, должна быть последней лексемой в начальной строке (однако, это не оз- начает, что она должна предшествовать первому элементу списка);
- в множественное определение нельзя включать такие директи- вы, как IF или INCLUDE.
В режиме MASM продолжение строки можно использовать при вы- боре VERSION M51,M520. При это строки и другие лексемы можно раз- мещать на нескольких строках, используя в качестве последнего символа строки символ "\". Например:
VERSION M51,M520 DB 'Hello out there \ you guys'
В любом месте строки вы можете использовать стандартный ре- жим продолжения строки Турбо Ассемблера (он всегда доступен). Он действует также, как комментарий, например:
ARG a1:word, \ первый аргумент a2:word, \ второй аргумент a3:word, \ третий аргумент

Программирование с объектами

Хорошо хранить процедуры метода отдельно от описаний метода (в отдельном файле) и отдельно от кода, использующего данный объ- ект. Мы рекомендуем помещать процедуры метода в файл с именем, совпадающим с именем объекта, и расширением .ASM. Например, про- цедуры метода для объекта связанного списка можно поместить в файл LIST.ASM. Файл процедур метода должен включать (с помощью INCLUDE) описания метода из файла .ASO.
В конце данной главы показан пример процедур метода объекта списка. Чтобы показать общую структуру файла, приведем фрагмент файла LIST.ASM (его можно найти в примерах на дистрибутивном дис- ке):
;---------------------------------------------------- ;-- Определение объекта связанного списка ----------- ;---------------------------------------------------- MODEL SMALL LOCALS
;** Определить объект связанного списка **
INCLUSE node.aso
;** Создать экземпляр таблицы виртуальных методов ** ;** связанного списка **
DATASEG
TBLINST
;** Методы связанного списка **
CODESEG
;;<<все процедуры методов>>
В общем случае следует использовать следующую форму объектно -ориентированного программирования в Турбо Ассемблере
--------------T-------------------------------------------------¬ ¦ Файл ¦ Содержимое ¦ +-------------+-------------------------------------------------+ ¦<объект>.ASO ¦ INCLUDE <порождающий_объект>.ASO (если он есть),¦ ¦ ¦ GLOBAL описание объекта и директива GLOBAL для¦ ¦ ¦ каждой процедуры методов. ¦ ¦ ¦ ¦ ¦<объект>.ASM ¦ INCLUDE <объект>.ASO содержит директиву TBLINST¦ ¦ ¦ и описание процедур методов, содержит метод init¦ ¦ ¦ c TBLINIT. ¦ L-------------+--------------------------------------------------
Заметим, что вы можете использовать директивы TBLINST и TBLINIT, даже если в объекте нет виртуальных методов. В этом слу- чае никаких действий не выполняется. Таким образом, мы рекоменду- ем вам использовать директивы TBLINST и TBLINIT независимо от на- личия в объекте виртуальных методов. Поместите директиву TBLINST в соответствующий сегмент данных, а TBLINIT - в метод инициализа- ции объекта (который должен быть статическим). Вы должны вызывать этот метод перед использованием всех других методов объекта.

Программы DOS

Программы DOS предназначены для работы под управлением опе- рационной системы DOS и записываются в двух форматах:
- формат .EXE;
- формат .COM.
Формат EXE позволяет использовать наиболее общую в DOS сег- ментацию программы. Программы могут иметь насколько сегментов и могут ссылаться на сегмент или группу сегментов по имени. Таким образом, программы .EXE могут превышать по размеру 64К.
Формат COM представляет собой достаточно простой формат. Программы в таком формате не могут содержать символьных ссылок на имена групп и сегментов. Таким образом, программы COM обычно пи- шутся с использованием модели TINY и ограничены по размеру данных или кода 64 килобайтами.
Чтобы строить программы DOS, вам потребуется компоновщик (например, TLINK) и утилита построения программ (например, MAKE). Подробнее об утилитах рассказывается в Приложении D.

Программы Windows

Турбо Ассемблер можно использовать для создания прикладных программ Windows. Windows может работать либо в реальном режиме (на всех процессорах 8086) или в защищенном режиме (на процессоре 80286 и старше). Таким образом программа, написанная для Windows, может работать в защищенном режиме. С помощью директив CODESEG, DATASEG и UDATASEG следует аккуратно разделить код и данные и ис- пользовать директиву WARN PRO, чтобы отмечать любые проблемы с доступом, которые могут возникать во время ассемблирования. Нако- нец, в программах защищенного режима не следует пытаться устанав- ливать сегментные регистры в вычисленные значения параграфов сег- мента. Значениями сегментов в защищенном режиме не являются адреса параграфов. Вместо этого используются дескрипторы, которые не имеют смысла в прикладной программе.
Кроме Турбо Ассемблера и Турбо отладчика для создания эффек- тивных прикладных программ Windows требуются другие средства. В частности, вы должны располагать компилятором Borland C++ (либо Microsoft C 2.6 и Windows Software Dewelopment Kit). Прикладные программы Windows обычно требуют наличия утилиты-компилятора ре- сурсов (RC) этих пакетов. Должны быть также доступны стандартные библиотеки. В Windows также необходим компоновщик (например, TLINK) и утилита построения программ (например, MAKE).
Данное приложение содержит простейшие рекомендации по созда- нию прикладных программ Windows и динамически компонуемых библио- тек (DLL). Более полное описание прикладных программ Windows мож- но найти в "Руководстве пользователя по С++" и соответствующей документации по Windows.

Простые арифметические операции

Турбо Ассемблер поддерживает простые арифметические опера- ции. которые приведены в следующей таблице:
Простые арифметические операции Таблица 5.15 -------------------------------T--------------------------------¬ ¦ Выражение ¦ Значение ¦ +------------------------------+--------------------------------+ ¦ +выражение ¦ Выражение. ¦ ¦ ¦ ¦ ¦ -выражение ¦ Отрицание выражения. ¦ ¦ ¦ ¦ ¦ выражение_1 + выражение_2 ¦ Выражение_1 плюс выражение_2. ¦ ¦ ¦ ¦ ¦ выражение_1 - выражение_2 ¦ Выражение_1 минус выражение_2. ¦ ¦ ¦ ¦ ¦ выражение_1 * выражение_2 ¦ Выражение_1, умноженное на вы- ¦ ¦ ¦ ражение_2. ¦ ¦ ¦ ¦ ¦ выражение_1 / выражение_2 ¦ Выражение_1, деленное на выра- ¦ ¦ ¦ жение_2 (используются целые ¦ ¦ ¦ числа со знаком). Выражение_2 ¦ ¦ ¦ не может быть нулевым или пре- ¦ ¦ ¦ вышать по размеру 16 бит. ¦ ¦ ¦ ¦ ¦ выражение_1 MOD выражение_2 ¦ Остаток от деления выражения_1 ¦ ¦ ¦ на выражение_2. Применяются те ¦ ¦ ¦ же правила, что и при делении. ¦ L------------------------------+---------------------------------

Простые директивы определения данных

Вы можете определять данные с помощью директив DB, DW, DD, DQ, DF, DP или DT. Как показано в следующей таблице, эти директи- вы выделяют простые данные различного размера:
Директивы определения данных различного размера Таблица 12.1 ---------------T------------------------------------------------¬ ¦ Директива ¦ Значение ¦ +--------------+------------------------------------------------+ ¦ DB ¦ Определение данных размером в байт. ¦ ¦ ¦ ¦ ¦ DW ¦ Определение данных размером в слово. ¦ ¦ ¦ ¦ ¦ DD ¦ Определение данных размером в двойное слово. ¦ ¦ ¦ ¦ ¦ DQ ¦ Определение данных размером в четверное слово.¦ ¦ ¦ ¦ ¦ DF ¦ Определение данных размером в 6 байт (48-бито-¦ ¦ ¦ вый дальний указатель процессора 80386). ¦ ¦ ¦ ¦ ¦ DP ¦ Определение данных размером в 6 байт (48-бито-¦ ¦ ¦ вый дальний указатель процессора 80386). ¦ ¦ ¦ ¦ ¦ DT ¦ Определение данных размером в 10 байт. ¦ L--------------+-------------------------------------------------
Синтаксис поля "значение" для каждой из этих директив разли- чается и основывается на возможности представлять отдельные вели- чины с помощью данных определенного размера (например, байтовые данные нельзя интерпретировать, как число с плавающей точкой).
В директиве DB (байт) можно задавать следующие значения:
- Выражение-константу, имеющую значения в диапазоне от -128 до 255 (байты со знаком в диапазоне от -128 до +127; безз- наковые байтовые значения в диапазоне от 0 до 255).
- 8-битовое относительное выражение, использующее операции HIGH и LOW.
- Символьную строку из 1 или более символов с использование стандартного формата заключенной в кавычки строки. В этом случае определяется несколько байт, по одному на каждый символ строки.
Значениями директивы DW (слово) могут быть:
- Выражение-константа в диапазоне от -32768 до 65535 (слова со знаком в диапазоне от -32768 до 32767, беззнаковые сло- ва в диапазоне от 0 до 65535).
- Относительное выражение, занимающее 16 или менее бит (включая смещение в 16-битовом сегменте, сегмент или зна- чение группы).

- Одно- или двухбайтовая строка в стандартном формате (стро- ка, заключенная в кавычки).

Значениями директивы DD (двойное слово) могут быть:

- Выражение-константа в диапазоне от -2147483648 до 4294967295 ( при выборе процессора 80386) или от -32768 до 65535 в противном случае.

- Относительное адресное выражение, состоящее из 16-битового сегмента и 16-битового смещения.

- Строка длиной до 4 символов в стандартном формате (строка, заключенная в кавычки).

Значениями директивы DQ (четверное слово) могут быть:

- Выражение-константа в диапазоне от -2147483648 до 4294967295 (при выборе процессора 80386) или от -32768 до 65535 в противном случае.

- Относительное или адресное выражение, состоящее из 32 или менее бит (при выборе процессора 80386) или 16 или менее бит (для всех других процессоров).

- Положительная или отрицательная константа, имеющая значе- ние в диапазоне от -2 с 63 степени до 2 в степени 63-1 (четверное слово со знаком в диапазоне от 2 в 63 степени до 2 в степени 63-1, беззнаковое четверное слово в диапа- зоне от 0 до 2 в степени 64-1).

- Строка длиной до 8 байт в стандартном формате (строка, за- ключенная в кавычки).

Значениями директив DF и DP (48-битовый дальний указатель процессора 80386) могут быть:

- Выражение-константа в диапазоне от -2147483648 до 4294967295 (при выборе процессора 80386) или от -32768 до 65535 в противном случае.

- Относительное или адресное выражение, состоящее из 32 или менее бит (при выборе процессора 80386) или 16 или менее бит (для всех других процессоров).

- Относительное адресное выражение, состоящее из 16-битового сегмента и 16-битового смещения.

- Положительная или отрицательная константа, имеющая значе- ние в диапазоне от -2 в 47 степени до 2 в степени 48-1 (6- байтовое значение со знаком в диапазоне от 2 в 47 степени до 2 в степени 47-1, беззнаковое 6-байтовое значение в ди- апазоне от 0 до 2 в степени 48-1).

- Строка длиной до 6 байт в стандартном формате (строка, за- ключенная в кавычки).

Значениями директивы DT могут быть:


- Выражение-константа в диапазоне от -2147483648 до 4294967295 ( при выборе процессора 80386) или от -32768 до 65535 в противном случае.

- Относительное или адресное выражение, состоящее из 32 или менее бит (при выборе процессора 80386) или 16 или менее бит (для всех других процессоров).

- Относительное адресное выражение, состоящее из 16-битового сегмента и 16-битового смещения.

- Положительная или отрицательная константа, имеющая значе- ние в диапазоне от -2 в 79 степени до 2 в степени 80-1 (10 -байтовое значение со знаком в диапазоне от 2 в 79 степени до 2 в степени 79-1, беззнаковое 10-байтовое значение в диапазоне от 0 до 2 в степени 80-1).

- Строка длиной до 10 байт в стандартном формате (строка, заключенная в кавычки).

- Упакованная десятичная константа, имеющая значение в диа- пазоне от 0 до 99999999999999999999.

Примечание: При сохранении данных в памяти младшее значение всегда записывается перед старшим значением.

В некоторых случаях числовые и строковые константы в дирек- тивах определения простых данных отличаются от тех, которые встречаются в стандартных выражениях Турбо Ассемблера. Например, директивы DB, DP, DT и DQ воспринимают заключенные в кавычки строки, которые могут иметь большую длину, чем строки, восприни- маемые в выражениях.

Заключенные в кавычки строки выделяются одинарными (') или двойными (") кавычками. Внутри строки два ограничителя указывают, что данный символ-ограничитель должен быть частью строки, напри- мер:

'what''s up doc?'

представляет следующие символы:

what's up doc?

В качестве значения в директивах DD, DQ и DT можно указывать числа с плавающей точкой. Приведем некоторые примеры таких чисел:

1.0E30 ; означает 1.0x10^30 2.56E-21 ; означает 2.56х10^E-21 1.28E+5 ; означает 1.28х10^+5 0.025 ; означает .025

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

DD 1E30 ; допустимое значение с плавающей точкой в ; режиме MASM DD .123 ; допустимо только в режиме MASM


Примечание: Для ясности вы рекомендуем всегда исполь- зовать начальную цифру и десятичную точку.

Турбо Ассемблер допускает также указывать в директивах DD, DQ и DT кодированные вещественные числа. Кодированное веществен- ное число - это шестнадцатиричное число точно определенной длины. Суффикс R указывает, что число будет интерпретироваться, как ко- дированное вещественное число. Длина числа должна заполнять тре- буемое поле (плюс одна цифра, если начальной цифрой является 0. Например:

DD 12345678r ; допустимое число DD 012345678r ; допустимое число DD 1234567r ; недопустимое число (слишком ; короткое)

Другие значения суффиксов (D, H, O, B) действуют аналогично тем, которые указываются в обычных выражениях.

Некоторые простые директивы определения данных интерпретиру- ют другие числовые константы особым образом. Например, если вы не задаете основания для значения в директиве DT, то Турбо Ассемблер использует двоично-десятичное представление (BCD). Другие дирек- тивы предполагают десятичное значение:

DD 1234 ; десятичное DT 1234 ; BCD

Если значение представляет собой простую положительную или отрицательную константу, используемое по умолчанию основание (за- даваемое директивой RADIX) не применяется в директивах DD, DQ и DT. Например:

RADIX 16 DW 1234 ; шестнадцатиричное значение 1234 DD 1234 ; десятичное значение 1234

Примечание: Числовые константы и директива RADIX под- робнее описываются в Главе 5.

Процессор 80386 имеет возможность работы

Процессор 80386 имеет возможность работы в 16- или 32-раз- рядном режиме. Многие стандартные инструкции в этих разных режи- мах имеют разный смысл. В Турбо Ассемблере размером инструкции можно управлять с помощью используемых в выражениях переопределе- ний SMALL и LARGE.

В общем случае, если вы в адресном выражении используете SMALL и LARGE, операция управляет генерацией адресной части инс- трукции в зависимости от того, должна она быть 16- или 32-разряд- ной.

Примечание: Более подробно о переопределении размера с помощью операций SMALL и LARGE рассказывается в Главе 5.

Когда SMALL или LARGE указывается вне адресной части выраже- ния, то можно управлять тем, какая инструкция выполняется - 16- или 32-битовая. В тех случаях, когда размер инструкции определя- ется по типу операнда, Турбо Ассемблер сам выбирает размер инс- трукции. Действие SMALL и LARGE показано в следующей таблице.

Примечание: Турбо Ассемблер выбирает размер инструк- ции, используя SMALL и LARGE, только когда нет другой ин- формации.

Действие инструкций SMALL и LARGE Таблица 13.6 ------------------------------T---------------------------------¬ ¦ Инструкция ¦ Действие ¦ +-----------------------------+---------------------------------+ ¦ PUSH[SMALL/LARGE] сегм_рег ¦ Выбирает, какая форма сегментно-¦ ¦ ¦ го регистра (16- или 32-разряд-¦ ¦ ¦ ная) используется в инструкции¦ ¦ ¦ PUSH. ¦ ¦ ¦ ¦ ¦ POP[SMALL/LARGE] сегм_рег ¦ Выбирает, какая форма сегментно-¦ ¦ ¦ го регистра (16- или 32-разряд-¦ ¦ ¦ ная) используется в инструкции¦ ¦ ¦ POP. ¦ ¦ ¦ ¦ ¦ FSAVE[SMALL/LARGE] указ_пам ¦ Выбирает, какая форма состояния¦ ¦ ¦ с плавающей точкой (16- или¦ ¦ ¦ 32-разрядная) сохраняется. ¦ ¦ ¦ ¦ ¦ FRSTOR[SMALL/LARGE] указ_пам¦ Выбирает, какая форма состояния¦ ¦ ¦ с плавающей точкой (16- или¦ ¦ ¦ 32-разрядная) восстанавливается.¦ ¦ ¦ ¦ ¦ FSTENV[SMALL/LARGE] указ_пам¦ Выбирает, какая форма состояния¦ ¦ ¦ с плавающей точкой (16- или¦ ¦ ¦ 32-разрядная) записывается. ¦ ¦ ¦ ¦ ¦ FLDENV[SMALL/LARGE] указ_пам¦ Выбирает, какая форма состояния¦ ¦ ¦ с плавающей точкой (16- или¦ ¦ ¦ 32-разрядная) загружается. ¦ ¦ ¦ ¦ ¦ LGDT[SMALL/LARGE] указ_пам ¦ Выбирает, какая форма глобальной¦ ¦ ¦ таблицы дескрипторов (16- или 32¦ ¦ ¦ -разрядная) загружается. ¦ ¦ ¦ ¦ ¦ SGDT[SMALL/LARGE] указ_пам ¦ Выбирает, какая форма глобальной¦ ¦ ¦ таблицы дескрипторов (16- или 32¦ ¦ ¦ -разрядная) сохраняется. ¦ ¦ ¦ ¦ ¦ LIDT[SMALL/LARGE] указ_пам ¦ Выбирает, какая форма таблицы¦ ¦ ¦ дескрипторов прерываний (16- или¦ ¦ ¦ 32-разрядная) загружается. ¦ ¦ ¦ ¦ ¦ SIDT[SMALL/LARGE] указ_пам ¦ Выбирает, какая форма таблицы¦ ¦ ¦ дескрипторов прерываний (16- или¦ ¦ ¦ 32-разрядная) сохраняется. ¦ ¦ ¦ ¦ ¦ JMP[SMALL/LARGE] указ_пам ¦ Для адресов памяти размером в¦ ¦ ¦ двойное слово (DWORD) выбирает¦ ¦ ¦ между 16-битовым переходом JMP¦ ¦ ¦ типа FAR и 32-битовым переходом¦ ¦ ¦ JMP типа NEAR. ¦ ¦ ¦ ¦ ¦ CALL[SMALL/LARGE] указ_пам ¦ Для адресов памяти размером в¦ ¦ ¦ двойное слово (DWORD) выбирает¦ ¦ ¦ между 16-битовой инструкцией¦ ¦ ¦ CALL типа FAR и 32-битовой инс-¦ ¦ ¦ трукцией CALL типа NEAR. ¦ L-----------------------------+----------------------------------

Расширенные инструкции перехода

Условные переходы, такие как JC или JE в процессорах 8086, 80186 и 80286 могут быть только ближними (NAER), то есть переход выполняется в границах сегмента и на расстояние -128 байт +127 байт относительно текущего адреса. Это ограничение действует и для условных инструкций цикла, таких как JCXZ или LOOP (на всех процессорах фирмы Intel).
Там, где это необходимо, Турбо Ассемблер может генерировать дополнительные последовательности переходов и обходить это огра- ничение. Например, Турбо Ассемблер может преобразовать инструк- цию:
JC xxx
в инструкции:
JNC temptag JMP xxx
Вы можете разрешить данную дополнительную последовательность переходов в помощью директивы JUMPS, и запретить ее директивой NOJUMPS. По умолчанию Турбо Ассемблер не генерирует это средство.
Когда вы указывает директиву JUMPS, Турбо Ассемблер резерви- рует достаточно места для дополнительных последовательностей пе- рехода во всех условных переходах вперед. Когда определяется фак- тическое расстояние перехода вперед, дополнительная последова- тельность может не понадобиться. Когда это происходит, Турбо Ас- семблер для заполнения лишнего пространства генерирует инструкции NOP.
Чтобы избежать дополнительных инструкций NOP, вы можете:
- использовать переопределение условных переходов, диапазон которых вам известен, например:
JC SHORT abc ADD ax,ax abc:
- задать параметр командной строки /m (подробнее о нем расс- казывается в Главе 2).

Расширенные инструкции PUSH и POP

Турбо Ассемблер поддерживает несколько расширений инструкций PUSH и POP. Эти расширения существенно уменьшают объем ввода, не- обходимого для задания расширенной последовательности инструкций PUSH и POP.

Расширенные инструкции сдвига

При использовании процессор 8086 инструкции сдвига RCL, RCR, ROL, ROR, SHL, SHR, SAL и SAR не могут воспринимать константу циклического сдвига, отличную от 1. При работе на процессорах 80186, 80286 и 80386 можно использовать константу циклического сдвига со значением до 255.
Когда Турбо Ассемблер обнаруживает инструкцию сдвига со зна- чением константы, большим 1 (при выборе процессора 8086),он гене- рирует соответствующее число инструкций сдвига со значением конс- танты циклического сдвига 1. Например, инструкции:
.8086 SHL ax,4
генерируют последовательность:
SHL ax,1 SHL ax,1 SHL ax,1 SHL ax,1

Различимость строчных и прописные символов в идентификаторах

В именах идентификаторов Турбо Ассемблер обычно не различает строчные и прописные буквы (верхний и нижний регистр). Поскольку в С++ они различаются, желательно задать такое различие и в Турбо Ассемблере (по крайней мере для тех идентификаторов, которые сов- местно используются Ассемблером и С++). Это можно сделать с по- мощью параметров /ML и /MX.
Переключатель (параметр) командной строки /ML приводит к тому, что в Турбо Ассемблере во всех идентификаторах строчные и прописные символы будут различаться (считаться различными). Пара- метр командной строки /MX указывает Турбо Ассемблеру, что строч- ные и прописные символы (символы верхнего и нижнего регистра) нужно различать в общедоступных (PUBLIC) идентификаторах, внешних (EXTRN) идентификаторах глобальных (GLOBAL) идентификаторах и об- щих (COMM) идентификаторах. В большинстве случаев следует также использовать параметр /ML.

Record field too large

(Слишком длинное поле в записи)
В определении записи сумма длин всех полей превышает 32 би- та. Например:
AREC RECORD RANGE:12,TOP:12,BOTTOM:12

Record member not found

(Не найден статический элемент записи)
Элемент записи задан в заполнителе указанной записи, который не является частью заданной записи.

Recursive definition not allowed for EQU

(Рекурсивное определение не допустимо в директиве EQU)
В выражении директивы EQU содержится то же самое имя, кото- рое определяется этой директивой. Например:
ABC EQU TWOTIMES ABC

Register must be AL or AX

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

Register must be DX

(Допустимо указание только регистра DX)
Неверен операнд команды. Допускается использовать только ре- гистр DX. Например:
IN AL,cx ; ошибка: вместо СХ должен быть указан регистр DX

Регистры

Имена регистров представляют регистры процессоров семейства 89086 и могут использоваться в составе выражения, например:
5+ax+7
При вычислении данного выражения получается значение ax+12, так как AX - это зарезервированный в Турбо Ассемблере идентифи- катор регистра. Идентификаторы регистров перечислены в следующем списке:
8086 AX, BX. CX, DX, SI, DI, DP, CS, DS, ES, SS
80186,80286 то же, что и для 8086
80386 регистры 8086, плюс EAX, EBX, ECX, EDX, ESI EDI, EBP, PS, GS, CR0, CR3, CR3, DR0, DR1, DR@, DR#, DR6, DR7
80486 регистры процессора 80386, плюс TR3, TR4, TR5

Relative jump out of range by __ bytes

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

Relative quantity illegal

(Недопустимый относительный адрес)
Команда или директива содержит операнд, ссылающийся на адрес памяти таким способом, что эта ссылка не может быть разрешена на этапе ассемблирования. Такие ссылки в Турбо Ассемблере являются недопустимыми. Например:
DATA SEGMENT PUBLIC X DB 0 IF OFFSET X GT 127 ; на этапе ассемблирования ; адрес не известен

Reserved word used as symbol

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

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

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

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

Для тех, кто пытается приспособить для своих целей макроас- семблер MASM, эта глава является, вероятно, наиболее важной в данном руководстве. Кроме очень хорошей совместимости с синтак- сисом MASM, Турбо Ассемблер позволяет несколько сгладить неудоб- ства программирования с использованием MASM с помощью улучшенного режима (режима Ideal).
Кроме всего прочего режим Ideal позволяет вам, глядя только на исходный текст, определить, как будет вести себя выражение или операнд инструкции. При этом нет необходимости держать в памяти все тонкости MASM. С помощью улучшенного режима вы просто сможете писать ясные и четкие выражения, которые будут выполняться так, как это было задумано.
Режим Ideal использует почти все ключевые слова, операции и конструкции языка MASM. Это означает, что вы можете исследовать и использовать средства режима Ideal поочередно, не прибегая к изу- чению большого числа новых правил или ключевых слов. Все средства режима Ideal представляют собой расширения или модификацию воз- можностей, имеющихся в MASM.
Благодаря строгой проверке типа, выражения режима Ideal Тур- бо Ассемблера гораздо более понятны и менее способствуют получе- нию непредвиденных результатов. В результате многие из проблем MASM, о которых мы предупреждали вас в предыдущих главах, исчеза- ют под зорким оком режима Ideal.
Pежим Ideal содержит также ряд средств, облегчающих процесс программирования как для новичков, так и для опытных пользовате- лей. Вот некоторые из таких средств:
* дублирование имен элементов во множественных структурах;
* сложные выражения HIGH и LOW;
* предсказуемая обработка директив EQU;
* корректная обработка сгруппированных сегментов данных;
* улучшенная содержательность директив;
* хорошо воспринимаемые выражения, заключенные в квадратные скобки.

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

В зависимости от типа результата функции Турбо Паскаля возв- ращают свои результаты различными способами.
Результаты функции скалярного типа
Результаты функции скалярных типов возвращаются в регистрах центрального процессора (ЦП). Байтовые значения возвращаются в регистре AL, значения размером в 2 байта - в регистре AX, 4-байтовые значения - в паре регистров DX:AX (старшее слово нахо- дится в регистре DX).
Результаты функции вещественного типа
Результаты используемого в Турбо Паскале 6-байтового прог- раммно эмулируемого вещественного типа возвращаются в трех ре- гистрах ЦП. Наиболее значащее (старшее) слово возвращается в DX, среднее - в BX, а наименее значащее - в AX.
Результаты функции типов сопроцессора 8087
Результаты типов, использующихся сопроцессором 8087, возвра- щаются в регистре вершины стека ST(0) (или просто ST).
Результаты функции строкового типа
Результаты строкового типа возвращаются во временной рабочей области, выделяемой Турбо Паскалем перед вызовом. Указатель даль- него типа на эту область заносится в стек перед занесением перво- го параметра. Заметим, что этот указатель не является частью списка параметров.
Примечание: Не удаляйте из стека полученный в резуль- тате указатель, так как Турбо Паскаль ожидает, что после вызова он будет доступен.
Результаты функции типа указатель
Результаты указатель возвращаются в паре регистров DX:AX (сегмент:смещение).

Rotate count must be constant or CL

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

Rotate count out of range

(Недопустимое значение для счетчика сдвига)
В команде сдвига или циклического сдвига второй операнд пре- вышает допустимое значение. Например:
.8086
SHL DL,3 ; ошибка: в командах процессора 8086 ; возможен сдвиг только на один разряд .286 ROR ax,40 ; ошибка: максимальное допустимое ; значение для счетчика сдвига равно 31
Максимальное допустимое значение для счетчика сдвига в ко- мандах процессора 8086 равно 1, для других процессоров это значе- ние может быть равно 31.

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

Часть 1: "Использование Турбо Ассемблера":
Глава 1: "Начало работы в Турбо Ассемблере" сообщает о том, что необходимо сделать для установки Турбо Ассемблера в вашей системе.
Глава 2: "Использование директив и параметров" приводит в алфавитном порядке подробную информацию обо всех директивах Турбо Ассемблера, и показывает, как с их помощью можно управлять рабо- той Турбо Ассемблера.
Глава 3: "Общие принципы программирования" описывает отличие режимов Ideal и MASM, а также рассказывает о том, как использо- вать предопределенные идентификаторы, символы комментариев и т.д.
Глава 4: "Объектно-ориентированное программирование" расска- зывает как можно в языке Ассемблера использовать методы объектно- ориентированного программирования.
В Главе 5 "Использование выражений и значений идентификато- ров" описывается вычисление и определение выражений и операций.
Глава 6 "Выбор директив процессора и идентификаторов" расс- казывает, как генерировать код для конкретных процессоров.
Глава 7 "Использование моделей памяти программ и сегмента- ции" сообщает о моделях программ, генерации идентификаторов (символических имен), упрощенных определений сегментов и о поряд- ке сегментов.
В Главе 8: "Определение типов данных" поясняется как опреде- лять структуры, объединения, таблицы, встроенные записи и объек- ты.
В Главе 9: "Установка и использование счетчика адреса" опи- сывается как и для чего желательно использовать счетчик адреса, а также об определении таблиц.
В Главе 10: "Описание процедур" рассказывается о том как можно использовать различные типы процедур, а также как опреде- лять и использовать аргументы и локальные переменные.
В Главе 11: "Управление областью действия идентификаторов" обсуждается как можно ограничивать или расширять области, в кото- рой идентификатор имеет конкретное значение.
В Главе 12: "Определение данных" описываются простые дирек- тивы определения данных, рассказывается о создании экземпляров структур, объединений, записей, перечислимых типов данных и объ- ектов.

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

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

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

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

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

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

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

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

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

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

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

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

Сan't locate file __

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

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

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

Segment alignment not strict enough

(Выравнивание сегмента не достаточно точное)
Указано недопустимое значение для границы выравнивания сег- мента. Либо оно не является степенью двойки, либо специфицирует более точное выравнивание чем то, которое указано в директиве SEGMENT. Например:
DATA SEGMENT PARA ALIGN 32 ; ошибка: PARA означает только 16 ALIGN 3 ; ошибка: не является степенью двойки

Segment attributes illegally redefined

(Недопустимое переопределение атрибутов сегмента)
Директивой SEGMENT повторно открывается уже определенный ра- нее сегмент, при этом указываются новые значения для атрибутов этого сегмента. Например:
DATA SEGMENT BYTE PUBLIC DATA ENDS DATA SEGMENT PARA ; ошибка: ранее было указано ; выравнивание на байт DATA ENDS
При повторном открытии сегмента атрибуты либо должны иметь те же самые значения, либо вообще быть опущены. Если при повтор- ном открытии сегмента атрибуты не указаны, то используются атри- буты из предыдущего определения.

Segment name is superfluous

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

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

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

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

Символ % указывает Турбо Ассемблеру, что выражение нужно вы- числить. Ассемблер преобразует результат выражения к виду ASCII с текущим основанием. Используйте данный символ, когда в качестве макроаргумента вы хотите передать строку,представляющую вычислен- ный результат, а не само выражение. При этом используется следую- щий синтаксис:
%выражение
где "выражение" может быть либо выражением (использующим любые допустимые операнды и операции), или именем текстовой макрокоман- ды. Если это выражение, то создаваемым текстом будет результат выражения с текущим основанием. Если "выражение" - это текстовая макрокоманда, то создаваемым тестом будет текст, который предс- тавляет текстовая макрокоманда. Например, следующий фрагмент программы:
DEFSYM MACRO NUM TMP_&NUM: ENDNUM
TNAME EQU ; определение ; текстовой макрокоманды DEFSYM %5+4 DEFSYM %TNAME
приведет с следующему макрорасширению:
TMP_9: TMP_JUNK:

Символ !

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

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

Приведем синтаксис определения передаваемых процедуре аргу- ментов:
ARG аргумент [,аргумент] . [=идентификатор] [RETURNS аргумент] [,аргумент]]
При определении локальных переменных процедуры используется следующий синтаксис:
LOCAL аргумент [,аргумент] . [=идентификатор]
Отдельные аргументы имеют следующий синтаксис:
имя_аргумента [[выражение_счетчик_1]] [: сложный_тип [:выражение_счетчик_2]]
где "сложный_тип" - это тип данных аргумента. Он может быть либо простым типом, либо сложным выражением-указателем. Подробнее о синтаксисе сложных типов рассказывается в Главе 5.
Если вы не задаете поле "сложный_тип", Турбо Ассемблер пред- полагает WORD. При выборе 32-разрядной модели он предполагает DWORD.
"Выражение_счетчик_2" задает, сколько элементов данного типа определяет аргумент. Например, в определении аргумента:
ARG tmp:DWORD:4
определяется аргумент с именем "tmp", состоящий из 4 двойных слов.
По умолчанию "выражение_счетчик_2" имеет значение 1 (кроме аргументов типа BYTE. Так как вы не можете занести в стек байто- вое значение, для аргументов типа BYTE значение счетчика по умол- чанию равно 2, что обеспечивает для них в стеке размер в слово. Это согласуется с языками высокого уровня, которые интерпретируют передаваемые в качестве параметров символьные переменные. Если вы действительно хотите задать аргумент, как один байт в стеке, нуж- но явным образом определить значение поля "выражение_счетчик_2", равное 1. Например:
ARG realbyte:BYTE:1
"Выражение_счетчик_1" представляет собой число элементов массива. Общее пространство, резервируемое для аргумента в стеке, равно произведению "выражения_счетчик_2" на длину, заданную полем "тип_аргумента" и на "выражение_счетчик_1". Если поле "выражение_ счетчик_1" не задано, то по умолчанию оно равно 1. Общее число аргументов задает произведение "выражения"_счетчик_1" на "выраже- ние_счетчик_2".

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

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

Для описания процедур вы можете использовать директиву PROC. В режиме Ideal она имеет следующий синтаксис:
PROC [[модификатор_языка] язык] имя [расстояние] [ARG список_аргументов] [RETURN список_элементов]; [LOCAL список_аргументов] [USES список_элементов] . . . ENDP [имя]
В режиме MASM используется следующий синтаксис:
имя PROC [[модификатор_языка] язык] [расстояние] [ARG список_аргументов] [RETURN список_элементов]; [LOCAL список_аргументов] [USES список_элементов] . . . [имя] ENDP
Турбо Ассемблер также воспринимает для определения процедур синтаксис MASM. Подробнее о синтаксисе MASM рассказывается в Гла- ве 3.

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

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

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

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

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

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

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

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

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

Итак, теперь вы уже знаете, как обычно в С++ передаются па- раметры функциям: вызывающая программа заносит параметры (справа налево) в стек, вызывает функцию, и извлекает параметры из стека (отбрасывает их) после вызова. Borland C++ может также работать по соглашениям, принятым в Паскале. Согласно этим соглашениям па- раметры передаются слева направо, а отбрасывает параметры (из стека) вызываемая программа. Разрешить использование соглашений Паскаля в Borland C++ можно с помощью параметра командной строки -p или ключевого слова pascal.
Примечание: Более подробно соглашения о связях Паскаля рассматриваются в Главе 19.
Приведем пример функции на Ассемблере, в которой используют- ся соглашения Паскаля:
; ; Вызывается, как: TEST(i, j ,k) ; i equ 8 ; левый параметр j equ 6 k equ 4 ; правый параметр ; .MODEL SMALL .CODE PUBLIC TEST TEST PROC push bp mov bp,sp mov ax,[bp+i] ; получить i add ax,[bp+j] ; прибавить к i j sub ax,[bp+k] ; вычесть из суммы k pop bp ret 6 ; возврат, отбросить ; 6 байт параметров ; (очистка стека) TEST ENDP END
Заметим, что для очистки стека от передаваемых параметров используется инструкция RET 6.
На Рис. 18.5 показано состояние стека после выполнения инс- трукции MOV BP,SP:
. . . . . . ¦ ¦ +-----------------------+ SP -- ¦ BP вызывающей прогр. ¦ -- BP +-----------------------+ SP + 2 ¦ Адрес возврата ¦ BP + 2 +-----------------------+ SP + 4 ¦ k ¦ BP + 4 +-----------------------+ SP + 6 ¦ j ¦ BP + 6 +-----------------------+ SP + 8 ¦ i ¦ BP + 8 +-----------------------+ ¦ ¦ +-----------------------+ ¦ ¦ . . . . . .
Рис. 18.5 Состояние стека после инструкции MOV BP,SP
Соглашения по вызовам Паскаля требуют также, чтобы все внеш- ние и общедоступные идентификаторы указывались в верхнем регистре и без предшествующих подчеркиваний. Зачем может потребоваться ис- пользовать в программе на С++ соглашения по вызовам Паскаля? Программа, использующая соглашения Паскаля, занимает обычно нес- колько меньше места в памяти и работает быстрее, чем обычная программа на языке С++, так как для очистки стека от параметров не требуется выполнять n инструкций ADD SP.

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

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

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

Большинство языков высокого уровня требуют, чтобы вызываемые процедуры сохраняли определенные регистры. Это можно сделать, за- нося их в стек в начале процедуры и извлекая из стека в конце процедуры.
Турбо Ассемблер может автоматически генерировать код для сохранения и восстановления этих регистров, который включается в код начала и завершения. Эти регистры можно указать в операторе USES, который имеет следующий синтаксис:
USES элемент [,элемент] .
где "элемент" может быть регистром или состоящим из одной лексемы элементом данных, который можно заносить и извлекать из стека. В одной процедуре допускается использовать не более 8 элементов. Например:
. . . myproc PROC PASCAL NEAR ARG @source:DWORD,@dest:DWORD,@count:WORD USES cx,si,di,foo MOV cx,@count MOV foo,@count LES di,@dest LDS si,@source REP MOVSB ENDP . . .
О сохранении регистров в языках Си и Паскаль можно подробнее узнать в Главе 18 и 19.
Оператор USES можно использовать только в тех процедурах, которые используют языковые соглашения, отличные от NOLANGUAGE.

При взаимодействии Турбо Ассемблера и Borland C++ вызываемые из программы на языке С++ функции Ассемблера могут делать все что угодно, но при этом они должны сохранять регистры BP, SP, CS, DS и SS. Хотя при выполнении функции Ассемблера эти регистры можно изменять, при возврате из вызываемой подпрограммы они должны иметь в точности такие значения, какие они имели при ее вызове.
Регистры AX, BX, CX, DX и ES, а также флаги могут произвольно из- меняться.
Регистры DI и SI представляют собой особый случай, так как в Borland C++ они используются для регистровых переменных. Если в модуле С++, из которого вызывается ваша функция на Ассемблере, использование регистровых переменных разрешено, то вы должны сох- ранить регистры SI и DI, если же нет, то сохранять их не нужно.
Однако неплохо всегда сохранять эти регистры, независимо от того, разрешено или запрещено использование регистровых перемен- ных. Трудно заранее гарантировать, что вам не придется компоно- вать данный модуль Ассемблера с другим модулем на языке С++, или перекомпилировать модуль С++ с разрешением использования регист- ровых переменных. При этом вы можете забыть, что изменения нужно также внести и в код Ассемблера.

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

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

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

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

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

Для создания экземпляра именованного типа данных используйте в качестве директивы выделения данных имя типа. Предположим, нап- ример, что вы определили следующий тип:
NNTYPE TYPEDEF PTR BYTE
Тогда оператор:
NNTEST NTTYPE ?
создает экземпляр именованного типа NTYPE (определяя переменную NTTEST). В данном примере, поскольку задано значение неинициали- зируемых данных ?, начальные данные в текущем сегменте не генери- руются.
Способ инициализации экземпляра именованного типа зависит от типа, который этот именованный тип представляет. Например, NTTYPE в предыдущем примере - это слово, поэтому он будет инициализиро- ваться, как если бы вы следующим образом использовали директиву DW:
NTTYPE 1,2,3 ; представляет указатель значений 1,2,3 DW 1,2,3 ; то же, что NTTYPE 1,2,3
Однако, если именованный тип представляет структуру или таб- лицу, то его нужно инициализировать также, как инициализируются структуры и таблицы. Например:
foo STRUC f1 DB ? ENDS bar TYPEDEF foo bar {f1=1} ; должен быть инициализатор структуры

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

Экземпляр данных перечислимого типа вы можете создать, ис- пользую как директиву определения данных имя перечислимого типа данных. Предположим, например, что вы определили следующее:
ETYPE ENUM FEE,FIE,FOO,FUM
Тогда оператор:
ETEST ETYPE ?
будет создавать экземпляр перечислимого типа данных ETYPE (опре- деляя переменную ETEST). В данном примере, поскольку задано зна- чение неинициализируемых данных ?, начальные данные в текущем сегменте не генерируются.
Экземпляры перечислимого типа данных всегда представляют со- бой байт, слово или двойное слов, в зависимости от максимального значения, представленного в данных перечислимого типа.

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

Чтобы создать экземпляр объекта, вы можете вызвать метод конструктора объекта (который выделяет память для экземпляра объ- екта), или распределить экземпляр объекта в предопределенном сег- менте данных.
Вы можете создать экземпляр объекта точно также, как вы соз- даете экземпляр структуры. Рассмотрите, например, следующие эк- земпляры объектов:
foolist list () ; экземпляр списка fooqueue label queue queue () ; экземпляр очереди queue (list_head=mynode,list_tail=mynode) ; экземпляр очереди
Когда вы создаете экземпляр объекта, вы можете переопреде- лить любые используемые по умолчанию в объекте данные, значения которых определены в описании объекта, переопределив эти значения в скобках. Однако переопределить методы при создании экземпляра объекта нельзя.

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

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

Чтобы создать экземпляр типа данных, представляющего собой структуру или объединения, используйте в качестве директивы опре- деления данных имя структуры или объединения. Предположим, нап- ример, что вы определили следующее:
ASTRUC STRUC B DB "xyz" C DW 1 D DD 2 ASTRUC ENDS
BUNION UNION X DW ? Y DD ? Z DB ? BUNION ENDS
Тогда операторы:
ATEST ASTRUC ? BTEST BUNUION ?
будут создавать экземпляры структуры astruc (определяя переменную atest) и объединения bunion (определяя переменную btest). Пос- кольку в примере указывается значение неинициализированных данных ?, то начальные данные в текущий сегмент не вводятся.

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

Для каждого объекта, содержащего виртуальные методы, необхо- димо наличие доступной таблицы виртуальных методов. Правильное размещение данной таблицы определяет множество факторов, включая то, какую программную модель вы используете, хотите вы получить таблицу NEAR или FAR и т.д. Турбо Ассемблер требует от вас только размещения данной таблицы. Экземпляр последних определенных объ- ектов вы можете создать, используя псевдооперацию TBLINST, кото- рая имеет следующий синтаксис:
TBLINST
TBLINST определяет в качестве адреса таблицы виртуальных ме- тодов объекта @TableAddr_<имя_объекта>. Это эквивалентно следую- щему:
@TableAddr_<имя_объекта> @TableAddr_<имя_объекта> {}
Назад | Содержание | Вперед

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

Для создания экземпляра таблицы используйте в качестве ди- рективы выделения данных имя таблицы. Предположим, например, что вы определили следующую таблицу:
TTYPE TABLE VIRTUAL MoveProc:WORD=MoveRtn, \продолжение. VIRTUAL MsgProc:DWORD=MsgRtn, \продолжение. VIRTUAL DoneProc:WORD=DoneRtn,
Тогда оператор:
TTEST TTYPE ?
создает экземпляр таблицы TTYPE (определяя переменную TTEST). В данном примере, поскольку задано значение неинициализируемых дан- ных ?, начальные данные в текущем сегменте не генерируются.

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

Чтобы создать экземпляр данных типа записи, используйте имя данного типа, как директиву выделения данных. Например, предполо- жим, что вы определили следующее:
MYREC RECORD VEL:3=4,MODE:2,SIZE:4=15
Тогда оператор:
MTEST MYREC ?
будет создавать экземпляр записи myrec (определяя переменную mtest). В данном примере данные в сегмент не помещаются, так как задаются неинициализированные данные (?).
Экземпляры записи всегда имеют размер в байт, слово или двойное слово, в зависимости от числа бит, выделенных при опреде- лении.

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

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

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

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

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

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

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

В режиме Ideal применяются следующие правила старшинства операций:
1. Наивысший приоритет (приоритет 1) имеют все ключевые сло- ва в первой позиции строки. Они проверяются первыми.
2. Ключевые слова на второй позиции строки имеют второй при- орите и рассматриваются во вторую очередь.

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

Правила синтаксического анализа строк в режиме MASM значи- тельно более сложны, чем в режиме Ideal. Вместо двух здесь имеет- ся три уровня приоритета:
1. Наивысший приоритет (приоритет 1) присваивается отдельным ключевым словам в первой позиции (таким как NAME и %OUT).
2. Следующий по старшинству приоритет (приоритет 2) имеют все идентификаторы, обнаруженные на второй позиции.
3. Все другие ключевые слова в первой позиции имеют наимень- ший (третий) приоритет.
Примечание: Внутри определений структур Турбо Ассемб- лер интерпретирует ключевые слова приоритета 1 как приори- тет 3. В этом случае ключевые слова приоритета 2 имеют выс- ший приоритет.
Например, в следующем фрагменте кода:
NAME SEGMENT
NAME - это ключевое слово первого приоритета, а SEGMENT - ключевое слово второго приоритета. Таким образом, Турбо Ассемблер будет интерпретировать эту строку, как директиву NAME, а не как директиву SEGMENT. В другом примере:
MOV INSTR,1
MOV - ключевое слово приоритета 3, а INSTR - ключевое слово прио- ритета 2. Таким образом, Турбо Ассемблер интерпретирует эту стро- ку как инструкцию INSTR, а не как инструкцию MOV (как вы можете предполагать).

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

Коснемся теперь проблемы организации интерфейса Турбо Ассем- блера с кодом языка С++, где используются директивы определения сегментов старого типа (стандартные директивы определения сегмен- тов). Например, если вы замените в модуле DOTOTAL.ASM упрощенные директивы определения сегментов директивами старого типа, то по- лучите следующее:
DGROUP group _DATA,_BSS _DATA segment word public 'DATA' EXTRN _Repetitions:WORD ; внешний идентификатор PUBLIC _StartingValue ; доступен для других модулей _StartValue DW 0 _DATA ends _BSS segment word public 'BSS' RunningTotal DW ? _BSS ends _TEXT segment byte public 'CODE' assume cs:_TEXT.ds:DGROUP,ss:DGROUP PUBLIC _DoTotal _DoTotal PROC ; функция (в малой модели памяти ; вызывается с помощью вызова ; ближнего типа) mov cx,[_Repetitions] ; счетчик выполнения mov ax,[_StartValue] mov [RunningTotal],ax ; задать начальное ; значение TotalLoop: inc [RunningTotal] ; RunningTotal++ loop TotalLoop mov ax,[RunningTotal] ; возвратить конечное ; значение (результат) ret _DoTotal ENDP _TEXT ENDS END
Данная версия директив определения сегментов не только длин- нее, то также и хуже читается. К тому же при использовании в программе на языке С++ различных моделей памяти ее труднее изме- нять. При организации интерфейса с Borland C++ в общем случае в использовании старых директив определения сегментов нет никаких преимуществ. Если же вы тем не менее захотите использовать при организации интерфейса с Borland C++ старые директивы определения сегментов, вам придется идентифицировать корректные сегменты, со- ответствующие используемым в коде на языке С++ моделям памяти.
Простейший способ определения, какие сегментные директивы старых версий должны выбираться для компоновки с той или иной программой Borland С++, заключается в компиляции главного модуля программы на Borland С++ для желаемой модели памяти с параметром -S, что тем самым заставит Borland С++ сгенерировать ассемблерную версию соответствующей программы на Borland С++. В этой версии кодов Си вы сможете найти все старые сегментные директивы, ис- пользуемые Турбо Cи; просто скопируйте их в вашу ассемблерную часть программы.
Вы также можете посмотреть, как будут выглядеть соответству- ющие старые директивы, скомпилировав их обычным образом (без па- раметра -S) и использовав TDUMP - утилиту, поставляемую Турбо Ас- семблером, чтобы получить все записи определения сегмента. Ис- пользуйте следующую командную строку:
tdump -OI segdef module.obj

String too long

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

Строки

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

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

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

Symbol already defined:__

(Имя идентификатора уже определено)
Указанное имя идентификатора уже было ранее объявлено с тем же самым типом. Например:
BB DB 1,2,3 BB DB ? ; ошибка: BB уже определено

Symbol already different kind

(Имя идентификатора уже объявлено с другим типом)
Указанное идентификатора имя было ранее объявлено с другим типом, например:
BB DB 1,2,3 BB DW ? ; ошибка: BB уже объявлено с типом BYTE

Symbol has no width or mask

(Имя идентификатора не может быть использовано в операциях WIDTH и MASK)
Операнд операции WIDTH или MASK не является именем записи или именем поля записи. Например:
B DB 0 mov ax,MASK B ; В не является полем записи

Symbol is not a segment or already part of a group

(Имя идентификатора не является именем сегмента либо уже оп- ределено в группе)
Либо имя идентификатора уже определено в группе, либо не яв- ляется именем сегмента. Например:
DATA SEGMENT DATA ENDS DGROUP GROUP DATA DGROUP2 GROUP DATA ; ошибка: DATA уже определено в DGROUP

Таблица виртуальных методов

Таблица виртуальных методов (ТВМ) представляет собой табли- цу адресов процедур, которые выполняют виртуальные методы. Обычно данная таблица размещается в сегменте данных программы. Любой объект, содержащий виртуальные методы, требует, чтобы где-либо в программе находился экземпляр таблицы виртуальных методов.
Для создания экземпляра таблицы виртуальных методов объекта используется директива TBLINST. Поскольку эта директива создает таблицу для последнего описанного объекта, ее следует помещать непосредственно после описания объекта, например:
INCLUDE list.aso DATASEG TBLINST

Текстовые макрокоманды

Текстовая макрокоманда - это идентификатор, представляющий строку текстовых символов. Когда Турбо Ассемблер обнаруживает идентификатор в выражениях (и в других ситуациях), он подставляет вместо него текстовые символы. Например, если DoneMsg - это текс- товая макрокоманда, имеющая значение "Returning to DOS", то сле- дующий оператор:
GoodBy DB DoneMsg
дает в результате:
GoodBy DB 'Returning to DOS'

Текстовые справочные файлы

В состав Турбо Ассемблера входят несколько справочных фай- лов: FILELIST.DOC. README, HELPME!.TSM, MANUAL.TSM, H2ASH.TSM и TCREF.TSM. Первые два файла содержатся на установочной дискете "Installation Disk" и копируются в основной каталог языка. Другие файлы находятся в подкаталоге DOC.

Тело макрокоманды, состоящей из нескольких строк

Независимо от содержимого тела макрокоманды, средство обра- ботки макрокоманд Турбо Ассемблера интерпретирует макрокоманду, состоящую из нескольких строк, как просто несколько строк текста. Турбо Ассемблер позволяет вам заменить идентификаторы в теле мак- рокоманды текстом, заданным при вызове макрокоманды. Это средство называется подстановкой аргументов. Идентификаторы в теле макро- команды, которые должны быть заменены, называются формальными (фиктивными) аргументами. Предположим, например, что идентифика- тор foo - это формальный аргумент в следующем теле макрокоманды:
PUSH foo MOV foo,1
Если при вызове данной макрокоманды вы присваиваете foo текстовую строку AX, то фактическим включаемым в модуль текст бу- дет:
PUSH foo MOV AX,1
Правила, которые Турбо Ассемблер использует для распознава- ния формальных аргументов, достаточно сложны. Рассмотрим, напри- мер, следующие строки тела макрокоманды, где формальный аргумент foo не распознается:
symfoo: DB 'It is foo time'
В общем случае Турбо Ассемблер не будет распознавать фор- мальный аргумент (без специального указания) в следующих ситуаци- ях:
- когда он является частью другого идентификатора;
- когда он находится внутри кавычек (' или ");
- в режиме Ideal, когда он указывается после двоеточия не внутри кавычек.

Терминология

Для различных единиц объектно-ориентированного программиро- вания в C++ и Паскале используются различные термины. В этом смысле Турбо Ассемблер более близок к Паскалю, хотя не все терми- ны здесь совпадают. Различия между языками описываются в следую- щей таблице:
Терминология объектно-ориентированного программирования Таблица 4.1 ------------------------T----------------------T----------------¬ ¦Турбо Ассемблер ¦ Borland C++ ¦ Турбо Паскаль ¦ +-----------------------+----------------------+----------------+ ¦метод ¦ функция-элемент ¦ метод ¦ ¦ ¦ ¦ ¦ ¦процедура метода ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦объект ¦ класс ¦ объект ¦ ¦ ¦ ¦ ¦ ¦базовый объект ¦ базовый класс ¦ базовый объект ¦ ¦ ¦ ¦ ¦ ¦порождающий объект ¦ порождающий класс ¦ порождающий ¦ ¦ ¦ ¦ объект ¦ ¦ ¦ ¦ ¦ ¦порожденный объект ¦ порожденный класс ¦ порожденный ¦ ¦ ¦ ¦ объект ¦ ¦ ¦ ¦ ¦ ¦поле порожденного ¦ элемент данных ¦ поле ¦ ¦ объекта ¦ ¦ ¦ L-----------------------+----------------------+-----------------
Примечание: Эти термины подробнее поясняются в данной главе ниже.

Text macro expansion exceeds maximum line length

(Расширение текстовой макрокоманды превышает максимально до- пустимую длину)
Это сообщение об ошибке выдается, когда расширение текстовой макрокоманды превышает максимально допустимую длину.

Типы идентификаторов

Каждый идентификатор имеет тип, который описывает его харак- теристики и связанную с ним информацию. Тип задается способом оп- ределения идентификатора. Например, вы можете определить иденти- фикатор, представляющий числовое выражение, текстовую строку, имя процедуры или переменную. Типы идентификаторов, поддерживаемых Турбо Ассемблером, перечислены в Таблице 5.4.
Типы идентификаторов Таблица 5.4 ----------------------------T------------------------------------¬ ¦ Тип идентификатора ¦ Описание ¦ +---------------------------+------------------------------------+ ¦ адрес ¦ Адрес. Подтипами данных являются ¦ ¦ ¦ типы UNKNOWN, BYTE, WORD, DWORD, ¦ ¦ ¦ PWORD, QWORD, TBYTE и адрес струк- ¦ ¦ ¦ туры или таблицы с указанным име- ¦ ¦ ¦ нем. Подтипами кода являются ¦ ¦ ¦ SHORT, NEAR, FAR. ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ текстовая макрокоманда ¦ Текстовая строка. ¦ ¦ ¦ ¦ ¦ псевдоним ¦ Эквивалентный идентификатор. ¦ ¦ ¦ ¦ ¦ числовое выражение ¦ Значение числового выражения. ¦ ¦ ¦ ¦ ¦ макрокоманда из ¦ Несколько текстовых строк с пус- ¦ ¦ нескольких строк ¦ тыми аргументами. ¦ ¦ ¦ ¦ ¦ структура/объединение ¦ Тип данных структуры или объеди- ¦ ¦ ¦ нения. ¦ ¦ ¦ ¦ ¦ таблица ¦ Табличный тип данных. ¦ ¦ ¦ ¦ ¦ элемент структуры/ ¦ Элемент структуры или таблицы. ¦ ¦ таблицы ¦ ¦ ¦ ¦ ¦ ¦ запись ¦ Данные типа записи. ¦ ¦ ¦ ¦ ¦ поле записи ¦ Поле записи. ¦ ¦ ¦ ¦ ¦ перечисление ¦ Перечислимый тип данных. ¦ ¦ ¦ ¦ ¦ сегмент ¦ Сегмент. ¦ ¦ ¦ ¦ ¦ группа ¦ Группа. ¦ ¦ ¦ ¦ ¦ тип ¦ Названный тип. ¦ L---------------------------+-------------------------------------

Типы меток

Хотя в программах Турбо Ассемблера можно свободно обращаться к любой переменной или данным любого размера (8, 16, 32 бита и т. д.), в общем случае хорошо обращаться к переменным в соответствии с их размером. Например, если вы записываете слово в байтовую пе- ременную, то обычно это приводит к проблемам:
. . . SmallCount DB 0 . . . mov WORD PTR [SmallCount],0ffffh . . .
Поэтому важно, чтобы в операторе Ассемблера EXTRN, в котором описываются переменные С++, задавался правильный размер этих пе- ременных, так как при генерации размера доступа к переменной С++ Турбо Ассемблер основывается именно на этих описаниях.
Если в программе на языке С++ содержится оператор:
char c
то код Ассемблера:
. . . EXTRN c:WORD . . . inc [c] . . .
может привести к весьма неприятным ошибкам, поскольку после того, как в коде на языке С++ переменная c увеличится очередные 256 раз, ее значение будет сброшено, а так как она описана, как пере- менная размером в слово, то байт по адресу OFFSET c + 1 будет увеличиваться некорректно, что приведет к непредсказуемым резуль- татам.
Между типами данных С++ а Ассемблера существует следующее соотношение:
--------------------------------T-------------------------------¬ ¦ Тип данных С++ ¦ Тип данных Ассемблера¦ +-------------------------------+-------------------------------+ ¦ unsigned char ¦ byte ¦ ¦ char ¦ byte ¦ ¦ enum ¦ word ¦ ¦ unsigned short ¦ word ¦ ¦ short ¦ word ¦ ¦ unsigned int ¦ word ¦ ¦ int ¦ word ¦ ¦ unsigned long ¦ dword ¦ ¦ long ¦ dword ¦ ¦ float ¦ dword ¦ ¦ double ¦ qword ¦ ¦ long double ¦ tbyte ¦ ¦ near* ¦ word ¦ ¦ far* ¦ dword ¦ L-------------------------------+--------------------------------

Турбо Паскаль использует те же

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

Точность в выражениях

Турбо Ассемблер в режиме Ideal всегда использует 32-разряд- ную арифметику. В режиме MASM Турбо Ассемблер использует 16- или 32-разрядную арифметику, в зависимости от выбора процессора 80386. Таким образом, некоторые выражения, в зависимости от того, какой процессор выбирается, могут давать разные результаты. Нап- ример, при вычислении:
(1000h * 1000h) / 1000h
получается 1000h при выборе процессора 80386 или 0 при выборе процессоров 8086, 80186 или 80286.

Too few operands to instruction

(В команде не хватает операндов)
В команде должно быть указано большее число операндов. Нап- ример:
ADD ax ; отсутствует второй операнд

Too many errors found

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

Too many errors or warnings

(Слишком много ошибок или предупреждений)
Сообщения об ошибках больше выдаваться не будут. Максималь- ное число сообщений об ошибках, которое выдается Турбо Ассембле- ром, равно 100. Это максимально допустимое число ошибок превыше- но. Ассемблирование программы тем не менее будет продолжено, однако далее будут печататься только предупреждающие сообщения.

Too many initial values

(Слишком много начальных значений)
При инициализации структуры или объединения указано слишком много значений. Например:
XYZ STRUC A1 DB ? A2 DB ? XYZ ENDS ANXYZ XYZ <1,2,3> ; ошибка: в XYZ определены ; только два поля
При инициализации полей структур и объединений допускается указывать меньше значений, чем число полей, но нельзя указывать больше значений.

Too many register multipliers in expression

(В выражении содержится слишком много множителей для регист- ров)
Для процессора 80386 допускается коэффициент индексных опе- рандов. Однако коэффициент можно указывать не более, чем у одного регистра. Например:
mov EAX,[2*EBX+4*EDX] ; слишком много ; коэффициентов

Too many registers in expression

(В выражении указано слишком много регистров)
В выражении указано более одного индексного и одного базис- ного регистра. Например:
mov ax,[BP+SI+DI] ; нельзя одновременно указывать SI и DI

Too many USES registers

(Слишком много регистров в директиве USES)
В директиве USES для текущей процедуры указано более 8 ре- гистров.

Trailling null value assumed

(Предполагается конечное пустое значение)
Директива определения данных (DB, DW и т.д.) оканчивается запятой. TASM интерпретирует ее как пустое значение. Например:
db 'привет',13,10 ; то же, что и db'привет',13,10?

Требования к программному и аппаратному обеспечению

Турбо Ассемблер работает на компьютерах семейства IBM PC, включая модели XT, AT и PS/2, а также на полностью совместимых с ними компьютерах. Для работы Турбо Ассемблера требуется операци- онная система MS-DOS (версии 2.0 или более поздняя) и не менее 256К оперативной памяти.
Турбо Ассемблер генерирует инструкции процессоров 8086, 80186, 80286, 80386 и i486, а также инструкции с плавающей точкой для арифметических сопроцессоров 8087, 80287 и 80387. (Подробнее об инструкциях процессором семейства 80х86/80х87 рассказывается в книгах фирмы Intel.)

Турбо Ассемблер 3.0/tasm/#1-2. Руководство пользователя.

Введение

Требования к программному и аппаратному обеспечению

Поддержка DPMI

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

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

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

Как обратиться за помощью к фирме Borland

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

Возможности фирмы Borland

ЧАСТЬ 1. ИСПОЛЬЗОВАНИЕ ТУРБО АССЕМБЛЕРА

Глава 1. Начало работы на Турбо Ассемблере

Установка Турбо Ассемблера в системе

Текстовые справочные файлы

Файл README

Файл HELPME!.TSM

Файл H2ASH.TSM

Файл TCREF.TSM

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

Ваша первая программа на Турбо Ассемблере

Ассемблирование вашей первой программы

Компоновка программы

Запуск вашей первой программы

Что происходит?

Модификация вашей первой программы на Турбо Ассемблере

Вывод информации на устройство печати

Ваша вторая программа на Турбо Ассемблере

Запуск программы REVERSE.ASM

Литература по языку Ассемблера

Глава 2. Использование директив и параметров

Запуск Турбо Ассемблера из DOS

Параметры командной строки

Параметр /A

Параметр /B

Параметр /C

Параметр /D

Параметр /E

Параметр /H или /?

Параметр /I

Параметр /J

Параметр /KH

Параметр /L

Параметр /LA

Параметр /M

Параметр /ML

Параметр /MU

Параметр /MV#

Параметр /MX

Параметр /N

Параметр /O

Параметр /OP

Параметр /P

Параметр /Q

Параметр /R

Параметр /S

Параметр /T

Параметр /V

Параметр /W

Параметр /X

Параметр /Z

Параметр /ZD

Параметр /ZI

Параметр /ZN

Косвенные командные файлы

Файлы конфигурации

Глава 3. Общие принципы программирования

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

Для чего используется режим Ideal?

Переключение в режим Ideal и выход из него

Отличия режима Ideal и режима MASM

Выражения и операнды

Операции

Подавление корректировок

Операнд инструкции BOUND

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

Доступ к данным в сегменте, принадлежащем группе

Комментарии в программах

Комментарии в конце строки

Директива COMMENT

Продолжение строки

Использование включаемых файлов

Предопределенные идентификаторы

Присваивание идентификаторам значений

Общая структура модуля

Директива VERSION


Директива NAME

Директива END

Вывод сообщения в процессе ассемблирования

Вывод предупреждающих сообщений

Вывод нескольких сообщений об ошибках

Глава 4. Объектно-ориентированное программирование

Терминология

Для чего в Турбо Ассемблере используются объекты?

Что такое объект?

Пример объекта

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

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

Таблица виртуальных методов

Инициализация таблицы виртуальных методов

Вызов метода объекта

Вызов статического метода

Вызов виртуального метода

Вызов виртуальных методов "предков"

Кое-что еще о вызове методов

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

Программирование с объектами

Пример из области программирования

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

Константы

Числовые константы

Изменение используемого по умолчанию основания

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

Идентификаторы

Имена идентификаторов

Типы идентификаторов

Адресные подтипы данных

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

Выражения

Точность в выражениях

Константы в выражениях

Идентификаторы в выражениях

Регистры

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

Значения простых идентификаторов

Унарная операция LENGTH

Унарная операция WIDTH

Унарная операция MASK

Общие арифметические операции

Простые арифметические операции

Логические арифметические операции

Поразрядные операции сдвига

Операции сравнения

Задание адресного подтипа выражения

Получение типа выражения

Переопределение сегментной части адресного выражения

Получение сегмента и смещения адресного выражения

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

Определение характеристик выражения

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

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

Подразумеваемое сложение

Получение значения старшего или младшего байта выражения

Задание 16- или 32-разрядных выражений

Глава 6. Директивы выбора процессора и идентификаторы процессора



Директивы процессоров iAPx86

Предопределенные идентификаторы

Идентификатор @Cpu

Идентификатор @WordSize


Директивы сопроцессора 8087

Директивы эмуляции сопроцессора

Глава 7. Использование моделей памяти программы и сегментации

Директива MODEL

Идентификаторы, генерируемые директивой MODEL

Идентификатор @Model

Идентификатор @32Bit

Идентификатор @CodeSize

Идентификатор @DataSize

Идентификатор @Interface

Упрощенные сегментные директивы

Идентификаторы, создаваемые упрощенными директивами сегментации

Директива STARTUPCODE

Идентификатор @Startup

Директива EXITCODE

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

Директива SEGMENT

Атрибут комбинирования сегментов

Атрибут класса сегмента

Атрибут выравнивания сегмента

Атрибут размера сегмента

Атрибут доступа к сегменту

Директива ENDS

Директива GROUP

Директива ASSUME

Переопределение сегмента

Изменение упорядочивания сегментов в модуле

Директива .ALPHA

Директива .SEQ

Директива DOSSEG: упорядочивание сегментов в порядке DOS

Изменение размера стека

Глава 8. Определение типов данных

Определение перечислимых типов данных

Определение записей с битовыми полями

Определение структур и объединений

Открытие определения структуры или объединения

Задание элементов структуры и объединения

Определение меток элементов структур с помощью директивы LABEL

Выравнивание элементов структуры

Закрытие определения структуры или объединения

Вложенные структуры и объединения

Включение одной именованной структуры в другую

Использование имен структур в выражениях

Определение таблиц

Переопределение элементов таблицы

Определение именованного типа

Определение объекта

Директива TBLPTR

Идентификаторы, определяемые расширенной директивой STRUC

Глава 9. Задание и использование счетчика адреса

Идентификатор счетчика адреса $

Директивы счетчика адреса $

Директива ORG

Директивы EVEN и EVENDATA

Директива ALIGN

Определение меток

Операция :

Директива LABEL

Директива ::

Глава 10. Описание процедур

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

Описание процедур NEAR или FAR

Описание языка процедуры

Задание модификатора языка

Определения аргументов и локальных переменных


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

Область действия аргументов и имен локальных переменных

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

Вложенные процедуры и правила области действия

Описание процедур методов для объектов

Глава 11. Управление областью действия идентификаторов

Переопределяемые идентификаторы

Область действия в границах блока

Директивы LOCALS и NOLOCALS

Область действия в границах блока в MASM

Локальные метки в стиле MASM

Глава 12. Определение данных

Простые директивы определения данных

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

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

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

Инициализация экземпляров записи

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

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

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

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

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

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

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

Глава 13. Расширенные инструкции

Эффективная генерация кода: директивы SMART и NOSMART

Расширенные инструкции перехода

Дополнительные инструкции цикла процессора 80386

Дополнительные инструкции ENTER и LEAVE

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

Расширенные инструкции PUSH и POP

Инструкции PUSH и POP с несколькими операндами

Использование в инструкциях PUSH и POP указателей

Занесение в стек константа (процессор 8086)

Расширенные инструкции сдвига

Принудительное переопределение сегментов: инструкции SEGxx

Дополнительные инструкции работы с флагами

Дополнительные инструкции работы с битами полей

Инструкция GETFIELD

Дополнительная быстрая инструкции непосредственного умножения.

Расширение необходимых инструкций для процессора 80386

Вызов процедур с кадрами стека

Вызываемые процедуры, содержащие RETURNS

Вызов процедур методов для объектов: CALL...METHOD

Остаточная рекурсия для методов объектов: инструкция JMP...METHOD

Дополнительные инструкции для объектного программирования

Глава 14. Использование макрокоманд


Текстовые макрокоманды

Определение текстовых макрокоманд с помощью директивы EQU

Директивы макрокоманд работы с текстовыми строками

Директива CATSTR

Директива SUBSTR

Директива INSTR

Директива SIZESTR

Примеры макрокоманд работы с текстом

Макрокоманды, состоящие из нескольких строк

Тело макрокоманды, состоящей из нескольких строк

Использование в макрокомандах символа &

Включение комментариев в тело макрокоманды

Локальные формальные аргументы

Директива EXITM

Директива GOTO и макроидентификаторы перехода

Общие макрокоманды, состоящие из нескольких строк

Вызов общей макрокоманды, состоящей из нескольких строк

Литеральные строки в угловых скобках

Символ !

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

Переопределение общей макрокоманды, состоящей из нескольких строк

Удаление общей макрокоманды: директива PURGE

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

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

Директива WHILE

Макрокоманды повторения строк

Непосредственная макродиректива %

Включение расширения макрокоманды в файл листинга

Глава 15. Использование условных директив

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

Директива условного ассемблирования IFxxx

Директивы условного ассемблирования ELSEIFxxx

Директивы генерации сообщений об ошибке ERRxx

Описания конкретных директив

Безусловные директивы генерации сообщений об ошибке

Условные директивы с выражениями

Условные директивы с идентификатором

Условные директивы с текстовыми строками

Условия выполнения прохода Ассемблером

Включение условных директив в файл листинга

Глава 16. Интерфейс с компоновщиком

Описание внешних идентификаторов

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

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

Описание идентификаторов библиотеки

Определение внешних идентификаторов

Описание глобальных идентификаторов

Определение общих переменных

Включение библиотеки

Глава 17. Генерация листинга

Формат листинга

Общие директивы управления листингом

Директивы листинга включаемых файлов

Директивы вывода блоков условного ассемблирования


Директивы вывода в файл листинга макрокоманд

Директивы вывода листинга перекрестных ссылок

Параметры изменения формата листинга

Глава 18. Интерфейс Турбо Ассемблера и Borland C++

Вызов из Borland C++ функций Турбо Ассемблера

Основные моменты в интерфейсе Турбо Ассемблера и Borland C++

Компоновка ассемблерных модулей с С++

Использование Extern "C" для упрощения компоновки

Модели памяти и сегменты

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

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

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

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

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

Различимость строчных и прописные символов в идентификаторах

Типы меток

Внешние дальние идентификаторы

Командная строка компоновщика

Взаимодействие между Турбо Ассемблером и Borland C++

Передача параметров

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

Возврат значений

Вызов функции Турбо Ассемблера из Borland C++

Написание на языке Ассемблера функций-элементов С++

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

Вызов Borland C++ из Турбо Ассемблера

Компоновка с кодом инициализации С++

Задание сегмента

Выполнение вызова

Вызов из Турбо Ассемблера функции Borland C++

Глава 19. Интерфейс Турбо Ассемблера с Турбо Паскалем

Совместное с Турбо Паскалем использование данных

Директива компилятора $L и внешние подпрограммы

Директива PUBLIC

Директива EXTRN

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

Использование корректировок сегментов

Устранение неиспользуемого кода

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

Параметры-значения

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

Вещественные значения

Типы Single, Double, Extended и Comp и типы сопроцессора 8087

Указатели

Строки

Записи и массивы

Множества

Параметры-переменные

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

Доступ к параметрам

Использование для адресации к стеку регистра BP

Директива ARG

Турбо Паскаль и директива .MODEL

Использование другого базового или индексного регистра

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


Выделение пространства для локальных данных

Выделение статической памяти

Выделение временной памяти

Примеры подпрограмм на Ассемблере для Турбо Паскаля

Подпрограмма шестнадцатиричного преобразования общего назначения

Пример обмена содержимого двух переменных

Пример анализа операционной среды DOS

Часть 2. ПРИЛОЖЕНИЯ 1

Приложение A. Замечания по программированию

Упрощенные директивы определения сегмента

Программы DOS

Замечания по программам формата EXE

Замечания по программам формата COM

Программы Windows

Замечания по динамически компонуемым библиотекам Windows

Замечания по прикладным программам Windows

Приложение B. Обзор синтаксических правил Турбо Ассемблера

Лексическая грамматика

Грамматика выражений в режиме MASM

Грамматика выражений в режиме Ideal

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

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

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

Ключевые слова и предопределенные идентификаторы

Ключевые слова директив

Приложение C. Вопросы совместимости

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

Переменные операционной среды

Формат фирмы Microsoft для двоичных чисел с плавающей точкой

Приложение D. Утилиты Турбо Ассемблера

Утилита-преобразователь H2ASH

Приложение E. Сообщения об ошибках

Информационные сообщения

Предупреждающие сообщения и сообщения об ошибках

32-bit segment not allowed without .386

Argument needs type override

Argument to operation or instruction has illegal size

Arithmetic overflow

ASSUME must be segment register

Bad keyword in SEGMENT statement

Can't add relative quantities

Can't address with currently ASSUMEd segment registers

Can't convert to pointer

Can't emulate 8087 instruction

Can't make variable public

Can't override ES segment

Can't subtract dissimilar relative quantities

Can't use macro name in expression

Can't use this outside macro

Code or data emission to undeclared segment

Constant assumed to mean Immediate const

Constant too large

CS not correctly assumed

CS override in protected mode


CS unreachable from current segment

Declaration needs name

Directive ignored in Turbo Pascal model

Directive not allowed inside structure definition

Duplicate dummy arguments:_

ELSE or ENDIF without IF

Expecting METHOD keyword

Expecting offset quantity

Expecting offset or pointer quantity

Expecting pointer type

Expecting record field name

Expecting register ID

Expecting scalar type

Expecting segment or group quantity

Extra characters on line

Forward reference needs override

Global type doesn't match symbol type

ID not member of structure

Illegal forward reference

Illegal immediate

Illegal indexing mode

Illegal instruction

Illegal instruction for currently selected processor(s)

Illegal local argument

Illegal local symbol prefix

Illegal mаcro argument

Illegal memory reference

Illegal number

Illegal origin address

Illegal override in structure

Illegal override register

Illegal radix

Illegal register for instruction

Illegal register multiplier

Illegal segment address

Illegal use of constant

Illegal use of register

Illegal use of segment register

Illegal USES register

Illegal version ID

Illegal warning ID

Instruction can be compacted with override

Invalid model type

Invalid operand(s) to instruction

Labels can't start with numeric characters

Line too long - truncated

Location counter overflow

Method call requires object name

Missing argument list

Missing argument or <

Missing argument size variable

Missing COMM ID

Missing dummy argument

Missing end quote

Missing macro ID

Missing module name

Missing or illegal language ID

Missing or illegal type specifier

Missing table member ID

Missing term in list

Missing text macro

Model must be specified first

Module is pass-dependant - compatibility pass was done

Name must come first

Near jump or call to different CS

Need address or register

Need angle brackets for structure fill

Need colon

Need expression

Need file name after INCLUDE

Need left parenthesis

Need method name


Need pointer expression

Need quoted string

Need register in expression

Need right angle bracket

Need right curly bracket

Need right parenthesis

Need right square bracket

Need stack argument

Need structure member name

Not expecting group or segment quantity

One non-null field allowed per union expansion

Only one startup sequence allowed

Open conditional

Open procedure

Open segment

Open structure definition

Operand types do not match

Operation illegal for static table member

Pass-dependant construction encountered

Pointer expression needs brackets

Positive count expecting

Record field too large

Record member not found

Recursive definition not allowed for EQU

Register must be AL or AX

Register must be DX

Relative jump out of range by __ bytes

Relative quantity illegal

Reserved word used as symbol

Rotate count must be constant or CL

Rotate count out of range

Segment alignment not strict enough

Segment attributes illegally redefined

Segment name is superfluous

String too long

Symbol already defined:__

Symbol already different kind

Symbol has no width or mask

Symbol is not a segment or already part of a group

Text macro expansion exceeds maximum line length

Too few operands to instruction

Too many errors or warnings

Too many initial values

Too many register multipliers in expression

Too many registers in expression

Too many USES registers

Trailling null value assumed

Undefined symbol

Unexpected end of file (no END directive)

Unknown character

Unmatched ENDP:_

Unmatched ENDS:_

User-generated error

Value out of range

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

Bad switch

Can't find @file __

Сan't locate file __

Error writing to listing file

Error writing to object file

File not found

File was changed or deleted while assembly in progress

Insufficient memory to process command line

Internal error

Invalid command line

Invalid number after _

Out of hash space

Out of memory

Out of string space

Too many errors found

Unexpected end of file (no END directive)

Дополнение для Borland C++ 3.1. Новые средства Турбо ассемблера версии 3.1

Вперед >>>

Турбо Паскаль и директива .MODEL

Директива .MODEL с параметром TPASCAL задает упрощенную сег- ментацию, модель памяти и языковую поддержку. Обычно используется большая модель памяти (large) Ранее мы уже видели, что нужно сде- лать в программах Ассемблера, чтобы можно было использовать про- цедуры и функции Паскаля. Преобразуем пример, используя в нем ди- рективы .MODEL и PROC:
.MODEL large, PASCAL .CODE MyProc PROC FAR i:BYTE,j:BYTE result:DWORD PUBLIC MyProc mov ax,i . . . ret
Заметим, что теперь не нужно задавать параметры в обратном порядке. Не требуется также масса других операторов. Использова- ние в директиве .MODEL ключевого слова PASCAL задает использова- ние соглашений Паскаля, определяет имена сегментов, выполняет инструкции PUSH BP и MOV BP,SP и задает также возврат с помощью инструкций POP BP и RETn (где n - число байт параметров).

Удаление общей макрокоманды: директива PURGE

Для удаления макрокоманд вы можете использовать директиву PURGE. Директива PURGE имеет следующий синтаксис:
PURGE имя_макрокоманды, [имя_макрокоманды].
Директива PURGE удаляет определение макрокоманды, состоящей из нескольких строк, с именем "имя_макрокоманды" После использо- вания директивы PURGE Турбо Ассемблер больше не интерпретирует идентификатор PURGE как макрокоманду, например:
ADD MACRO a1,a2 SUB a1,a2 ENDM ADD ax,bx ; этот вызов даст SUB ax,bx PURGE ADD ADD ax,bx ; теперь это не макрокоманда, поэтому ; вы получите ADD ax,bx
Вы можете сразу удалить этой директивой несколько макроко- манд, разделив их имена запятыми. Отметим однако, что идентифика- тор удаленной макрокоманды можно переопределить только другой макрокомандой.

Указатели

Значения параметров для всех типов указателей заносятся не- посредственно в стек, как указатели дальнего типа: сначала слово, содержащее сегмент, затем другое слово, содержащее смещение. Сег- мент занимает старший адрес, в соответствии с соглашениями фирмы Intel. Для извлечения параметра-указателя в программе Турбо Ас- семблера можно использовать инструкции LDS или LES.

Унарная операция LENGTH

Унарная операция LENGTH возвращает информацию о счетчике или числе величин, представляющих идентификатор. Возвращаемое факти- ческое значение зависит от типа идентификатора, что показано в приведенной ниже таблице:
Значения, возвращаемые операцией LENGTH Таблица 5.11 --------------------------------T-------------------------------¬ ¦ Выражение ¦ Значение ¦ +-------------------------------+-------------------------------+ ¦ LENGTH имя_адреса ¦ Возвращает счетчик элементов, ¦ ¦ ¦ выделяемых при определении ¦ ¦ ¦ имени адреса. ¦ ¦ ¦ ¦ ¦ LENGTH имя_элемента_структуры/¦ Возвращает счетчик элементов, ¦ ¦ объединения ¦ выделенных при определении ¦ ¦ ¦ элемента (только режим MASM). ¦ L-------------------------------+--------------------------------
При применении ее ко всем другим типам идентификаторов опе- рация LENGTH возвращает значение 1. Приведем некоторые примеры использования операции LENGTH:
MSG DB "Hello" array DW 10 DUP (4 DUP (1),0) numbrs DD 1,2,3,4 lmsg = LENGTHG msg ; =1, нет операции DUP larray = LENGTH nsg ; =10, счетчик повторения DUP lnumbrs = LENGTH numbrs ; =1, нет операции DUP
Унарная операция SIZE
Унарная операция SIZE возвращает информацию о размере выде- ленного элемента данных. Возвращаемое значение зависит от типа заданного идентификатора. Список доступных для операции SIZE зна- чений приведен в следующей таблице: Значения, возвращаемые операцией SZIE Таблица 5.12 ------------------------------T---------------------------------¬ ¦ Выражение ¦ Значение ¦ +-----------------------------+---------------------------------+ ¦ SIZE имя_адреса ¦ В режиме Ideal возвращается ¦ ¦ ¦ фактическое число байт, выде- ¦ ¦ ¦ ленных для переменных данных. ¦ ¦ ¦ В режиме MASM возвращается ¦ ¦ ¦ размер подтипа имя_адреса ¦ ¦ ¦ (UNKNOWN=0, BYTE=1, WORD=2, ¦ ¦ ¦ DWORD=4, PWORD=FWORD=6, QWORD ¦ ¦ ¦ =8, TBYTE=10, SHORT=NEAR= ¦ ¦ ¦ 0ffffh, FAR=0fffeh, адрес ¦ ¦ ¦ структуры = размеру структу- ¦ ¦ ¦ ры), умноженный на значение ¦ ¦ ¦ LENGTH имя_адреса. ¦ ¦ SIZE имя_структуры/ ¦ Возвращает число байт, требу- ¦ ¦ объединения ¦ емых для представления струк- ¦ ¦ ¦ туры или объединения. ¦ ¦ SIZE имя_таблицы ¦ Возвращает число байт, необ- ¦ ¦ ¦ ходимых для представления ¦ ¦ ¦ таблицы. ¦ ¦ SIZE имя_элемента_таблицы/ ¦ Возвращает величину TYPE имя ¦ ¦ структуры ¦ _элемента_таблицы/структуры* ¦ ¦ ¦ LENGTH имя_элемента_таблицы/ ¦ ¦ ¦ объединения (только для ре- ¦ ¦ ¦ жима MASM). ¦ ¦ SIZE имя_записи ¦ Возвращает число байт, не- ¦ ¦ ¦ обходимых для представления ¦ ¦ ¦ общего числа бит, зарезерви- ¦ ¦ ¦ рованных в определении запи- ¦ ¦ ¦ си: 1, 2 или 4. ¦ ¦ SIZE имя_перечисления ¦ Возвращает число байт, необ- ¦ ¦ ¦ ходимых для представления ¦ ¦ ¦ максимального значения, при- ¦ ¦ ¦ сутствующего в перечислении: ¦ ¦ ¦ 1, 2 или 4. ¦ ¦ SIZE имя_сегмента ¦ Возвращает размер сегмента ¦ ¦ ¦ в байтах. ¦ ¦ SIZE имя_типа ¦ Возвращает число байт, необ- ¦ ¦ ¦ ходимых для представления ¦ ¦ ¦ названного типа, при этом ¦ ¦ ¦ ближние и дальние метки воз- ¦ ¦ ¦ вращают значение 0ffffh, а ¦ ¦ ¦ дальние - 0fffeh. ¦ L-----------------------------+----------------------------------
При применении ко всем другим типам идентификаторов операция SIZE возвращает значение 0.

Унарная операция MASK

Унарная операция MASK создает маску на основе битового поля, в котором биты в возвращаемом значении устанавливаются в 1 и со- ответствуют битам в поле, которое представляет идентификатор. Возвращаемое значение зависит от типа идентификатора, что показа- но в приведенной ниже таблице. Заметим, что для всех других иден- тификаторов использовать операцию MASK не разрешается.
Значения, возвращаемые MASK Таблица 5.14 --------------------------T-------------------------------------¬ ¦ Выражение ¦ Значение ¦ +-------------------------+-------------------------------------+ ¦ MASK имя_записи ¦ Возвращает маску, в которой биты, ¦ ¦ ¦ резервированные для представления ¦ ¦ ¦ битовых полей в определении записи, ¦ ¦ ¦ равны 1, а остальные равны 0. ¦ ¦ ¦ ¦ ¦ MASK имя_поля_записи ¦ Возвращает маску, в которой биты, ¦ ¦ ¦ резервированные для поля в определе-¦ ¦ ¦ нии записи, равны 1, а остальные ¦ ¦ ¦ равны 0. ¦ ¦ ¦ ¦ ¦ MASK имя_перечисления ¦ Возвращает маску, в которой биты, ¦ ¦ ¦ резервированные для представления ¦ ¦ ¦ максимального значения в определе- ¦ ¦ ¦ нии перечисления, равны 1, а ос- ¦ ¦ ¦ тальные равны 0. ¦ L-------------------------+--------------------------------------

Унарная операция WIDTH

Унарная операция WIDTH возвращает размер в битах поля запи- си. Это значение зависит от типа идентификатора. Эти типы иденти- фикаторов показаны в приведенной ниже таблице. Для всех других типов операцию WIDTH использовать не допускается.
Значения WIDTH Таблица 5.13 --------------------------T-------------------------------------¬ ¦ Выражение ¦ Значение ¦ +-------------------------+-------------------------------------+ ¦ WIDTH имя_записи ¦ Возвращает общее число бит, заре-¦ ¦ ¦ зервированных в определении записи.¦ ¦ ¦ ¦ ¦ WIDTH имя_поля_записи ¦ Возвращает число бит, зарезервиро-¦ ¦ ¦ ванных для поля в определении запи-¦ ¦ ¦ си. ¦ ¦ ¦ ¦ ¦ WIDTH имя_перечисления ¦ Возвращает число бит, необходимых ¦ ¦ ¦ для представления максимального ¦ ¦ ¦ значения в определении enum. ¦ L-------------------------+--------------------------------------

Undefined symbol

(Идентификатор не определен)
Оператор содержит идентификатор, который не был ранее опре- делен в исходном файле.

Unexpected end of file (no END directive)

(Неожиданный конец файла (нет директивы END))
В исходном файле отсутствует директива END. Директива END обязательно должна быть указана в конце исходного файла.

Unknown character

(Неизвестный символ)
Текущая строка исходной программы содержит символ, который не принадлежит набору символов, допустимых для построения симво- лических имен и выражений в Турбо Ассемблере. Например:
add ax,!1 ; ошибка: восклицательный знак - ; недопустимый символ

Unmatched ENDP:_

(Непарная директива ENDP:_)
В директиве ENDP указано имя, не совпадающее с именем проце- дуры, которую закрывает данная директива. Например:
ABC PROC XYZ ENDP ; ошибка: вместо XYZ должно быть указано ABC

Unmatched ENDS:_

(Непарная директива ENDS:_)
В директиве ENDS указано имя, не совпадающее с именем сег- мента, который закрывает данная директива, либо не совпадающее с именем структуры или объединения, оканчивающегося этой директи- вой. Например:
ABC STRUC XYZ ENDP ; ошибка: вместо XYZ должно быть указано ABC DATA SEGMENT CODE ENDS ; ошибка: вместо CODE должно быть указано DATA

и типы для модели памяти

Используемые по умолчанию сегменты и типы для модели памяти COMPACT Таблица A.4 ----------------------------------------------------------------¬ ¦ Директива Имя Выравнивание Комбинирование Класс Группа¦ +---------------------------------------------------------------+ ¦.CODE _TEXT WORD PUBLIC 'CODE' ¦ ¦.FARDATA FAR_DATA PARA private 'FAR_DATA' ¦ ¦.FARDATA? FAR_BSS PARA private 'FAR_BSS' ¦ ¦.DATA _DATA WORD PUBLIC 'DATA' DGROUP ¦ ¦.CONST CONST WORD PUBLIC 'CONST; DGROUP ¦ ¦.DATA? _BSS WORD PUBLIC 'BSS' DGROUP ¦ ¦STACK* STACK PARA STACK 'STACK' DGROUP ¦ ¦ ¦ ¦ * - STACK не подразумевается размещенным в DGROUP или¦ ¦FARSTACK, заданным в директиве MODEL. ¦ L----------------------------------------------------------------

Используемые по умолчанию сегменты и типы для модели памяти LARGE или HUGE Таблица A.5 ----------------------------------------------------------------¬ ¦ Директива Имя Выравнивание Комбинирование Класс Группа¦ +---------------------------------------------------------------+ ¦.CODE имя_TEXT WORD PUBLIC 'CODE' ¦ ¦.FARDATA FAR_DATA PARA private 'FAR_DATA' ¦ ¦.FARDATA? FAR_BSS PARA private 'FAR_BSS' ¦ ¦.DATA _DATA WORD PUBLIC 'DATA' DGROUP ¦ ¦.CONST CONST WORD PUBLIC 'CONST; DGROUP ¦ ¦.DATA? _BSS WORD PUBLIC 'BSS' DGROUP ¦ ¦STACK* STACK PARA STACK 'STACK' DGROUP ¦ ¦ ¦ ¦ * - STACK не подразумевается размещенным в DGROUP или¦ ¦FARSTACK, заданным в директиве MODEL. ¦ L----------------------------------------------------------------

Используемые по умолчанию сегменты и типы для модели памяти HUGE (TCHUGE) Borland C++ Таблица A.6 ----------------------------------------------------------------¬ ¦ Директива Имя Выравнивание Комбинирование Класс Группа¦ +---------------------------------------------------------------+ ¦.CODE имя_TEXT WORD PUBLIC 'CODE' ¦ ¦.FARDATA FAR_DATA PARA private 'FAR_DATA' ¦ ¦.FARDATA? FAR_BSS PARA private 'FAR_BSS' ¦ ¦.DATA имя_DATA PARA private 'DATA' ¦ ¦STACK* STACK PARA STACK 'STACK' ¦ ¦ ¦ ¦ * - STACK не подразумевается размещенным в DGROUP или¦ ¦FARSTACK, заданным в директиве MODEL. ¦ L----------------------------------------------------------------

Упрощенные директивы определения сегмента

В следующей таблице показаны используемые по умолчанию для каждой модели памяти атрибуты сегмента.
Используемые по умолчанию сегменты и типы для модели памяти TINY Таблица A.1 ----------------------------------------------------------------¬ ¦ Директива Имя Выравнивание Комбинирование Класс Группа¦ +---------------------------------------------------------------+ ¦.CODE _TEXT WORD PUBLIC 'CODE' DGROUP ¦ ¦.FARDATA FAR_DATA PARA private 'FAR_DATA' ¦ ¦.FARDATA? FAR_BSS PARA private 'FAR_BSS' ¦ ¦.DATA _DATA WORD PUBLIC 'DATA' DGROUP ¦ ¦.CONST CONST WORD PUBLIC 'CONST; DGROUP ¦ ¦.DATA? _BSS WORD PUBLIC 'BSS' DGROUP ¦ ¦STACK* STACK PARA STACK 'STACK' DGROUP ¦ ¦ ¦ ¦ * - STACK не подразумевается размещенным в DGROUP или¦ ¦FARSTACK, заданным в директиве MODEL. ¦ L----------------------------------------------------------------
Используемые по умолчанию сегменты и типы для модели памяти SMALL Таблица A.2 ----------------------------------------------------------------¬ ¦ Директива Имя Выравнивание Комбинирование Класс Группа¦ +---------------------------------------------------------------+ ¦.CODE _TEXT WORD PUBLIC 'CODE' ¦ ¦.FARDATA FAR_DATA PARA private 'FAR_DATA' ¦ ¦.FARDATA? FAR_BSS PARA private 'FAR_BSS' ¦ ¦.DATA _DATA WORD PUBLIC 'DATA' DGROUP ¦ ¦.CONST CONST WORD PUBLIC 'CONST; DGROUP ¦ ¦.DATA? _BSS WORD PUBLIC 'BSS' DGROUP ¦ ¦STACK* STACK PARA STACK 'STACK' DGROUP ¦ ¦ ¦ ¦ * - STACK не подразумевается размещенным в DGROUP или¦ ¦ FARSTACK, заданным в директиве MODEL. ¦ L----------------------------------------------------------------
Используемые по умолчанию сегменты и типы для модели памяти MEDIUM
Таблица A.3 ----------------------------------------------------------------¬ ¦ Директива Имя Выравнивание Комбинирование Класс Группа¦ +---------------------------------------------------------------+ ¦.CODE имя_TEXT WORD PUBLIC 'CODE' ¦ ¦.FARDATA FAR_DATA PARA private 'FAR_DATA' ¦ ¦.FARDATA? FAR_BSS PARA private 'FAR_BSS' ¦ ¦.DATA _DATA WORD PUBLIC 'DATA' DGROUP ¦ ¦.CONST CONST WORD PUBLIC 'CONST; DGROUP ¦ ¦.DATA? _BSS WORD PUBLIC 'BSS' DGROUP ¦ ¦STACK* STACK PARA STACK 'STACK' DGROUP ¦ ¦ ¦ ¦ * - STACK не подразумевается размещенным в DGROUP или ¦ ¦ FARSTACK, заданным в директиве MODEL. ¦ L----------------------------------------------------------------

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

Директива .MODEL указывает Турбо Ассемблеру, что сегменты, создаваемые с помощью упрощенных директив определения сегментов, должны быть совместимы с выбранной моделью памяти (TINY - крохот- ной, SMALL - малой, COMPACT - компактной, MEDIUM - средней, LARGEбольшой или HUGE - громадной) и управляет назначаемым по умолчанию типом (FAR или NEAR) процедур, создаваемых по директиве PROC. Модели памяти, определенные с помощью директивы .MODEL, совместимы с моделями Borland C++ с соответствующими именами.
Наконец, упрощенные директивы определения сегментов .DATA, .CODE, .DATA?, .FARDATA, .FARDATA? и .CONST генерируют сегменты, совместимые с Borland C++.
Например, рассмотрим следующий модуль Турбо Ассемблера с именем DOTOTAL.ASM:
.MODEL SMALL ; выбрать малую модель памяти ; (ближний код и данные) .DATA ; инициализация сегмента данных, ; совместимого с Borland C++ EXTRN _Repetitions:WORD ; внешний идентификатор PUBLIC _StartingValue ; доступен для других модулей _StartValue DW 0 .DATA? ; инициализированный сегмент ; данных, совместимый с Borland C++ RunningTotal DW ? .CODE ; сегмент кода, совместимый с ; Borland C++ PUBLIC _DoTotal _DoTotal PROC ; функция (в малой модели памяти ; вызывается с помощью вызова ; ближнего типа) mov cx,[_Repetitions] ; счетчик выполнения mov ax,[_StartValue] mov [RunningTotal],ax ; задать начальное ; значение TotalLoop: inc [RunningTotal] ; RunningTotal++ loop TotalLoop mov ax,[RunningTotal] ; возвратить конечное ; значение (результат) ret _DoTotal ENDP END
Написанная на Ассемблере процедура _DoTotal при использова- нии малой модели памяти может вызываться из Borland C++ с помощью оператора:
DoTotal();
Заметим, что в процедуре DoTotal предполагается, что где-то в другой части программы определена внешняя переменная Repetitions. Аналогично, переменная StartingValue объявлена, как общедоступная, поэтому она доступна в других частях программы. Следующий модуль Borland C++ (который называется SHOWTOT.CPP) об- ращается к данным в DOTOTAL.ASM и обеспечивает для модуля DOTOTAL.ASM внешние данные:

В следующей таблице показаны директивы выводы сообщения об ошибке, которые используют выражение с идентификаторами:

Директивы генерации сообщения об ошибке Таблица 15.5 -------------------------T--------------------------------------¬ ¦Директива IFxxx ¦ Ассемблирует "тело_условия_true" если¦ +------------------------+--------------------------------------+ ¦ERRIFDEF выражение_ид ¦ при вычислении выражения получается¦ ¦ ¦ значение true. ¦ ¦ ¦ ¦ ¦.ERRDEF выражение_ид ¦ при вычислении выражения получается¦ ¦ ¦ значение true (только для режима¦ ¦ ¦ MASM). ¦ ¦ ¦ ¦ ¦ERRIFNDEF выражение_ид ¦ при вычислении выражения получается¦ ¦ ¦ значение false. ¦ ¦ ¦ ¦ ¦.ERRNDEF выражение_ид ¦ при вычислении выражения получается¦ ¦ ¦ значение false (только в режиме¦ ¦ ¦ MASM). ¦ L------------------------+---------------------------------------

Например, следующие условия генерации сообщения об ошибке эквивалентны и приводят к генерации ошибки в случае определения foo и bar:

ERRIFDEF foo AND bar ERRIFNDEF NOT ( foo AND bar ) ERRIFBDEF NOT foo OR NOT bar

Упрощенные сегментные директивы

После выбора модели памяти вы можете использовать упрощенные сегментные директивы для того, чтобы начинать отдельные сегменты. Эти упрощенные директивы вы можете использовать только после ди- рективы MODEL, которая задает для модуля модель памяти. В модуле допускается указывать столько директив сегментации, сколько необ- ходимо. Турбо Ассемблер для получения одного сегмента комбинирует все части с одинаковыми именами (как если бы вы ввели все эти фрагменты после одной директивы сегментации). Перечень директив содержится в Таблице 7.4.
--------------------------T-------------------------------------¬ ¦ Директива ¦ Описание ¦ +-------------------------+-------------------------------------+ ¦ CODESEG [имя] ¦ Начинает или продолжает сегмент кода¦ ¦ ¦ модуля. Для моделей с дальним типом¦ ¦ ¦ кода вы можете задать имя, являющее-¦ ¦ ¦ ся фактическим именем сегмента. За-¦ ¦ ¦ метим, что таким образом вы можете¦ ¦ ¦ генерировать для модуля более одного¦ ¦ ¦ сегмента кода. ¦ ¦ ¦ ¦ +-------------------------+-------------------------------------+ ¦ .CODE [имя] ¦ Эквивалентна директиве CODESEG. До-¦ ¦ DATASEG ¦ пускается только для режима MASM.¦ ¦ ¦ Начинает или продолжает ближний или¦ ¦ ¦ инициализируемый по умолчанию сег-¦ ¦ ¦ мент данных. ¦ ¦ ¦ ¦ +-------------------------+-------------------------------------+ ¦ .DATA ¦ Эквивалентна директиве DATASEG. До-¦ ¦ CONST ¦ пускается только для режима MASM.¦ ¦ ¦ Начинает или продолжает сегмент¦ ¦ ¦ констант модуля. Постоянные данные¦ ¦ ¦ всегда являются ближними (NEAR) и¦ ¦ ¦ эквивалентны инициализированным дан-¦ ¦ ¦ ным. ¦ ¦ ¦ ¦ +-------------------------+-------------------------------------+ ¦ .CONST ¦ Эквивалентна директиве CONST. До-¦ ¦ UDATASEG ¦ пускается только для режима MASM.¦ ¦ ¦ Начинает или продолжает ближний или¦ ¦ ¦ не инициализируемый по умолчанию¦ ¦ ¦ сегмент данных. Соблюдайте осторож-¦ ¦ ¦ ность и включайте в этот сегмент¦ ¦ ¦ только неинициализированные данные,¦ ¦ ¦ в противном случае получаемая выпол-¦ ¦ ¦ няемая программа будет иметь больший¦ ¦ ¦ чем нужно размер. Описание распреде-¦ ¦ ¦ ления неинициализированных данных¦ ¦ ¦ можно найти в Главе 12. ¦ ¦ ¦ ¦ +-------------------------+-------------------------------------+ ¦ .DATA? ¦ Эквивалентна директиве UDATASEG.¦ ¦ ¦ Действует только для режима MASM. ¦ ¦ ¦ ¦ +-------------------------+-------------------------------------+ ¦ STACK [размер] ¦ Начинает или продолжает сегмент ¦ ¦ ¦ стека модуля. Необязательный пара-¦ ¦ ¦ метр "размер" определяет объем ре-¦ ¦ ¦ зервируемой для стека памяти (в сло-¦ ¦ ¦ вах). Если вы не задаете размер,¦ ¦ ¦ Турбо Ассемблер резервирует по умол-¦ ¦ ¦ чанию 200h слов (1 килобайт). ¦ ¦ ¦ ¦ ¦ ¦ В режиме MASM все метки, код или¦ ¦ ¦ данные, следующие за оператором¦ ¦ ¦ STACK, не будут рассматриваться как¦ ¦ ¦ часть сегмента стека. Однако в режи-¦ ¦ ¦ ме Ideal резервируется специальная¦ ¦ ¦ область, и сегмент стека остается¦ ¦ ¦ открытым, благодаря чему вы можете¦ ¦ ¦ добавлять метки и другие неинициали-¦ ¦ ¦ зированные данные. ¦ ¦ ¦ ¦ ¦ ¦ Директивы стека обычно требуется ис-¦ ¦ ¦ пользовать, если вы пишете на языке¦ ¦ ¦ Ассемблера автономную программу.¦ ¦ ¦ Большинство языков высокого уровня¦ ¦ ¦ сами создают для вас стек. ¦ ¦ ¦ ¦ +-------------------------+-------------------------------------+ ¦ .STACK [размер] ¦ Эквивалентна директиве STACK. Дей-¦ ¦ ¦ ствует в режиме MASM. ¦ ¦ ¦ ¦ +-------------------------+-------------------------------------+ ¦ FARDATE [имя] ¦ Начинает или продолжает дальний не-¦ ¦ ¦ инициализированный сегмент данных¦ ¦ ¦ (FAR) с заданным именем. Если вы не¦ ¦ ¦ задаете имя, Турбо Ассемблер исполь-¦ ¦ ¦ зует сегментное имя FAR_DATA. В мо-¦ ¦ ¦ дуле может содержаться более одного¦ ¦ ¦ неинициализированного сегмента дан-¦ ¦ ¦ ных типа FAR. ¦ ¦ ¦ ¦ +-------------------------+-------------------------------------+ ¦ .FARDATA [имя] ¦ Эквивалентна FARDATA. Действует в¦ ¦ ¦ режиме MASM. ¦ ¦ ¦ ¦ +-------------------------+-------------------------------------+ ¦ UFARDATA ¦ Начинает или продолжает дальний не-¦ ¦ ¦ инициализированный сегмент данных с¦ ¦ ¦ заданным именем. Если вы не задаете¦ ¦ ¦ имя, то Турбо Ассемблер использует¦ ¦ ¦ имя сегмента FAR_BSS. В модуле у вас¦ ¦ ¦ может быть несколько неинициализиро-¦ ¦ ¦ ванных сегментов данных типа FAR. ¦ ¦ ¦ ¦ +-------------------------+-------------------------------------+ ¦ .FARDATA? [имя] ¦ Эквивалентна UFARDATA. Действует¦ ¦ ¦ только в режиме MASM. ¦ L-------------------------+--------------------------------------
Примечание: Если вам требуется знать фактические име- на, имена классов и выравнивание сегментов, создаваемых по упрощенным директивам сегментации, см. Приложение A.

User-generated error

(Ошибка, сгенерированная пользователем)
Ошибка выдана в результате выполнения одной из директив ге- нерирования ошибки. Например:
.ERR ; попадание в это место является ошибкой
USES has no effect without language (USES игнорируется без спецификации языка)
Это предупреждающее сообщение выдается, если оператор USES используется без предварительной спецификации языка.

Условия выполнения прохода Ассемблером

Следующие директивы обеспечивают выполнение условного ас- семблирования или генерацию ошибки на основе текущего прохода Ас- семблера:
----------------------T-----------------------------------------¬ ¦ Директива IFxxx ¦ ассемблирует "тело_условия_true", если:¦ +---------------------+-----------------------------------------+ ¦ IF1 ¦ Турбо Ассемблер выполняет первый проход.¦ ¦ IF2 ¦ Турбо Ассемблер выполняет второй проход.¦ L---------------------+------------------------------------------
----------------------T-----------------------------------------¬ ¦ Директива ERRxxx ¦ генерирует ошибку user error, если: ¦ +---------------------+-----------------------------------------+ ¦ ERRIF1 ¦ Турбо Ассемблер выполняет первый проход.¦ ¦ ¦ ¦ ¦ .ERR1 ¦ Турбо Ассемблер выполняет первый проход¦ ¦ ¦ (только для режима MASM). ¦ ¦ ¦ ¦ ¦ ERRIF2 ¦ Турбо Ассемблер выполняет второй проход.¦ ¦ ¦ ¦ ¦ .ERR2 ¦ Турбо Ассемблер выполняет второй проход¦ ¦ ¦ (только для режима MASM). ¦ L---------------------+------------------------------------------
Обычно Турбо Ассемблер работает как однопроходный ассемблер. Если вы используете средство многопроходной работы Турбо Ассемб- лера (вызывая его с параметром командной строки /m), то при не- обходимости используется несколько проходов.
Поскольку Турбо Ассемблер всегда выполняет по крайней мере один проход, директива условного ассемблирования IF1 всегда будет ассемблировать код своего условного блока, а директивы .ERR1 и ERRIF1 всегда будут генерировать сообщение об ошибке (но только на первом проходе).
Если вы используете любую из этих директив и не разрешаете многопроходную работу, то Турбо Ассемблер будет генерировать для всех таких директив предупреждение "Pass dependent construction" ("Конструкция, зависящая от прохода"), чтобы предупредить вас о возможно опасном пропуске кода. Если вы разрешите многопроходную работу, Турбо Ассемблер будет выполнять точно два прохода и гене- рировать предупреждение Vaximum compatibility pass was done (Вы- полнен проход с целью обеспечения максимальной совместимости).

Условные директивы с идентификатором

Эти директивы обеспечивают условное ассемблирование или ге- нерацию сообщения об ошибке на основе того, является ли опреде- ленным один или более идентификаторов. Данные идентификаторы объ- единены в выражение с идентификаторами.
Выражение с идентификаторами - это выражение, состоящее из имен идентификаторов, булевских операций AND, OR, NOT и круглых скобок. В выражении с идентификаторами каждое имя идентификатора интерпретируется как булевское значение, при вычислении которого получается значение true, если идентификатор существует (опреде- лен), или false, если идентификатор не существует (даже если он определяется в модуле позднее). Турбо Ассемблер комбинирует эти значения, используя булевские операции, для получения конечного значения true или false. В своей простейшей форме выражение с идентификаторами содержит имя единственного идентификатора и дает при вычислении значение true, если этот идентификатор определен. Синтаксические правила в выражении с идентификаторами аналогичны тем, которые используются в выражениях Турбо Ассемблера. Напри- мер, если идентификатор foo определен, а идентификатор bar - нет, то при вычислении выражения с идентификаторами получаются следую- щие результаты:
Вычисление определенных и неопределенных идентификаторов
Таблица 15.3 ------------------------------------T---------------------------¬ ¦ Выражение с идентификаторами ¦ Результат ¦ +-----------------------------------+---------------------------+ ¦ foo ¦ True ¦ ¦ ¦ ¦ ¦ bar ¦ False ¦ ¦ ¦ ¦ ¦ not foo ¦ False ¦ ¦ ¦ ¦ ¦ not bar ¦ True ¦ ¦ ¦ ¦ ¦ foo OR bar ¦ True ¦ ¦ ¦ ¦ ¦ foo AND bar ¦ False ¦ ¦ ¦ ¦ ¦ NOT (foo AND bar) ¦ True ¦ ¦ ¦ ¦ ¦ NOT foo OR NOT bar ¦ True (то же, что ¦ ¦ ¦ (NOT foo) OR (NOT bar) ¦ L-----------------------------------+----------------------------
В следующей таблице показаны директивы, которые управляют ассемблированием и используют выражение с идентификаторами:
Директивы, использующие выражение с идентификаторами Таблица 15.4 -------------------------T--------------------------------------¬ ¦Директива IFxxx ¦ Ассемблирует "тело_условия_true" если¦ +------------------------+--------------------------------------+ ¦IFDEF выражение_ид ¦ при вычислении выражения получается¦ ¦ ¦ значение true. ¦ ¦ ¦ ¦ ¦IFNDEF выражение_ид ¦ при вычислении выражения получается¦ ¦ ¦ значение false. ¦ ¦ ¦ ¦ ¦ELSEIFDEF выражение_ид ¦ при вычислении выражения получается¦ ¦ ¦ значение true. ¦ ¦ ¦ ¦ ¦ELSEIFNDEF выражение_ид ¦ при вычислении выражения получается¦ ¦ ¦ значение false. ¦ L------------------------+---------------------------------------

Условные директивы с текстовыми строками

Эти директивы условное ассемблирование или генерацию сообще- ний об ошибке на основе содержимого текстовой строки. Текстовой строкой может быть либо строка, заключенная в угловые скобки (<>), либо имя текстовой макрокоманды с предшествующим символом %. Например:
; текстовая строка ABC %foo ; содержимое текстовой макрокоманды foo
Примечание: Об определении текстовых макрокоманд и ра- боте с ними рассказывается в Главе 14.
Директивы условного ассемблирования, использующие текстовую строку, показаны в следующей таблице:
Директивы условного ассемблирования, использующие текстовую строку Таблица 15.6 -----------------------T----------------------------------------¬ ¦ Директива IFxxx ¦ ассемблирует "тело_условия_true", если:¦ +----------------------+----------------------------------------+ ¦ IFNB текст_строка ¦ "текст_строка" не состоит из одних про-¦ ¦ ¦ белов. ¦ ¦ ¦ ¦ ¦ IFB текст_строка ¦ "текст_строка" не пуста. ¦ ¦ ¦ ¦ ¦ IFIDN текст_строка_1,¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ текст_строка_2 ¦ идентичны. ¦ ¦ ¦ ¦ ¦ IFINDI текст_строка_1¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ ,текст_строка_2 ¦ идентичны (регистр символов не учиты- ¦ ¦ ¦ вается. ¦ ¦ ¦ ¦ ¦ IFDIF текст_строка_1,¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ текст_строка_2 ¦ различны. ¦ ¦ ¦ ¦ ¦ IFDEFI текст_строка_1¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ ,текст_строка_2 ¦ различны (регистр символов игнорирует- ¦ ¦ ¦ ся. ¦ ¦ ¦ ¦ ¦ ELSEIFNB текст_строка¦ "текст_строка" не состоит из пробелов. ¦ ¦ ¦ ¦ ¦ ELSEIFB текст_строка ¦ "текст_строка" не пуста. ¦ ¦ ¦ ¦ ¦ ELSEIFIDN текст_стро-¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ ка_1, текст_строка_2¦ идентичны. ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ELSEIDNI текст_стро- ¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ ка_1,текст_строка_2 ¦ идентичны (регистр символов игнориру- ¦ ¦ ¦ ется). ¦ ¦ ¦ ¦ ¦ ELSEIFDIF текст_стро-¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ ка_1,текст_строка_2 ¦ различны. ¦ ¦ ¦ ¦ ¦ ELSEIFDIFI текст_стро¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ -ка_1,текст_строка_2¦ различны (регистр символов игнориру- ¦ ¦ ¦ ется). ¦ L----------------------+-----------------------------------------

Использующие текстовую строку директивы генерации сообщения об ошибке показаны в следующей таблице:

Директивы вывода сообщений об ошибке использующие текстовую строку Таблица 15.6 -------------------------T--------------------------------------¬ ¦Директива ERRxxx ¦ Генерирует сообщение об ошибке, если:¦ +------------------------+--------------------------------------+ ¦ERRIFNB текст_строка ¦ "текст_строка" не состоит из одних¦ ¦ ¦ пробелов. ¦ ¦ ¦ ¦ ¦.ERRIFNB текст_строка ¦ "текст_строка" не состоит из одних¦ ¦ ¦ пробелов (только для режима MASM). ¦ ¦ ¦ ¦ ¦ERRIFB текст_строка ¦ "текст_строка" состоит из пробелов. ¦ ¦ ¦ ¦ ¦.ERRB текст_строка ¦ "текст_строка" состоит из одних про-¦ ¦ ¦ белов. ¦ ¦ ¦ ¦ ¦ERRIFIDN текст_строка_1,¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ текст_строка_2 ¦ идентичны. ¦ ¦ ¦ ¦ ¦.ERRIDN текст_строка_1, ¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ текст_строка_2 ¦ идентичны (только для режима MASM). ¦ ¦ ¦ ¦ ¦ERRIFIDNI текст_строка_1¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ ,текст_строка_2 ¦ идентичны (регистр символов игнориру-¦ ¦ ¦ ется). ¦ ¦ ¦ ¦ ¦.ERRIDNI текст_строка_1,¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ текст_строка_2 ¦ идентичны (регистр символов игнориру-¦ ¦ ¦ ется) - только для режима MASM. ¦ ¦ ¦ ¦ ¦ERRIFDIF текст_строка_1,¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ текст_строка_2 ¦ различны. ¦ ¦ ¦ ¦ ¦.ERRDIF текст_строка_1, ¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ текст_строка_2 ¦ различны (только для режима MASM). ¦ ¦ ¦ ¦ ¦ERIFDIFI текст_строка_1,¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ текст_строка_2 ¦ различны (регистр символов игнориру- ¦ ¦ ¦ ется). ¦ ¦ ¦ ¦ ¦.ERRFIFI текст_строка_1,¦ "текст_строка_1" и "текст_строка_2" ¦ ¦ текст_строка_2 ¦ различны (регистр символов игнориру- ¦ ¦ ¦ ется) - только для режима MASM. ¦ L------------------------+---------------------------------------


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

При использовании их в макроопределениях директивы IFB и IFNB позволяют определить, передали ли вы в макрокоманду нужное число аргументов. Если при вызове макрокоманды вы передали слиш- ком мало аргументов, Турбо Ассемблер не генерирует сообщение об ошибке. Незаданные аргументы просто остаются пустыми. Таким обра- зом, вы можете определить макрокоманду, воспринимающую переменное число аргументов. Например:

. . . load MACRO addr, reg IFNB MOV reg,addr ELSE MOV ax,addr ENDIF ENDM . . .

Вы можете вызывать макрокоманду данного примера, как load test,cx, что даст генерацию инструкции mov cx,test, или вызвать ее как load test, то приведет к генерации инструкции mov ax,test, поскольку второй параметр пуст. Можно также использовать директи- ву ERRIFB для генерации сообщения об ошибке, если в вызове макро- команды пропущен аргумент:

. . . load MACRO addr ERRIFNB MOV reg,addr ENDM . . .

Если эту макрокоманду вызвать с помощью load, а не с помощью load test, то будет генерироваться ошибка.

Условные директивы с выражениями

Эти директивы обеспечивают условное ассемблирование или ге- нерацию сообщений об ошибке на основе результатов вычисления в Турбо Ассемблере выражения. Во всех этих директивах при вычисле- нии выражения должна получаться константа, и оно не может содер- жать опережающих ссылок. Если при вычислении выражения получается 0, Турбо Ассемблер рассматривает выражение, как ложное (false). в противном случае оно рассматривается как истинное (true).
В следующей таблице показаны директивы условного ассемблиро- вания, использующие выражения.
Директивы условного ассемблирования, использующие сообщения Таблица 15.1 ----------------------T-----------------------------------------¬ ¦ Директива IFxxx ¦ Ассемблирует "тело_условия_true" если: ¦ +---------------------+-----------------------------------------+ ¦ IF выражение ¦ при вычислении выражения получается ¦ ¦ ¦ значение true. ¦ ¦ ¦ ¦ ¦ IFE выражение ¦ при вычислении выражения получается ¦ ¦ ¦ значение false. ¦ ¦ ¦ ¦ ¦ ELSEIF выражение ¦ при вычислении выражения получается ¦ ¦ ¦ значение true. ¦ ¦ ¦ ¦ ¦ ELSEIFE выражение ¦ при вычислении выражения получается ¦ ¦ ¦ значение false. ¦ L---------------------+------------------------------------------
В следующей таблице показаны директивы генерации сообщений об ошибке, в которых используются выражения:
Директивы генерации сообщений об ошибке, использующие выражения
Таблица. 15.2 ---------------------T------------------------------------------¬ ¦ Директива ERRxxx ¦ Генерируется сообщение user error, если: ¦ +--------------------+------------------------------------------+ ¦ ERRIF выражение ¦ при вычислении выражения получается зна- ¦ ¦ ¦ чение true. ¦ ¦ ¦ ¦ ¦ .ERRNZ выражение ¦ при вычислении выражения получается зна- ¦ ¦ ¦ чение true (только в режиме MASM). ¦ ¦ ¦ ¦ ¦ ERRIFE выражение ¦ при вычислении выражения получается зна- ¦ ¦ ¦ чение false. ¦ ¦ ¦ ¦ ¦ .ERRE выражение ¦ при вычислении выражения получается зна- ¦ ¦ ¦ чение false (только в режиме MASM). ¦ L--------------------+-------------------------------------------

Установка Турбо Ассемблера в системе

Перед тем, как познакомиться с программированием на Турбо Ассемблере, вам нужно сделать следующее. Возьмите дистрибутивные диски Турбо Ассемблера и сделайте для каждого из них (с помощью утилиты DOS) рабочую копию. После этого исходные (дистрибутивные) диски уберите в надежное место.
Если собираетесь использовать Турбо Ассемблер вместо MASM (макроассемблер фирмы Microsoft), прочитайте Главу 2 и посмотри- те, в чем поведение Турбо Ассемблера отличается от MASM.
На диске INSTALL находится программа INSTALL.EXE, которая поможет вам установить Турбо Ассемблер версии 3.0 в вашей систе- ме. При установке следует учесть два параметра:
1. Hard Disk Users (пользователям системы с жестким диском): этот параметр позволяет выбрать подкаталоги, в которые будут загружены файлы.
2. Floppy Disk Users (пользователям системы с гибкими диска- ми): этот параметр установит файлы, необходимые для рабо- ты Турбо Ассемблера, в системе с двумя дисководами для гибких дисков. До начала работы подготовьте четыре отфор- матированных диска. Для начала установки перейдите в каталог с программой INSTALL и наберите команду:
INSTALL
Вам будут даны инструкции относительно каждого приглашения в рамке в нижней части экрана установки.
При запуске INSTALL в системе портативного компьютера или в любой другой системе с жидкокристаллическим дисплеем запуск INSTALL следует выполнять с параметром /b, который позволяет пе- ревести систему в черно-белый режим:
INSTALL /b

Устранение неиспользуемого кода

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

Утилита-преобразователь H2ASH

Модули Си и С++ в программе обычно совместно используют оп- ределение типов и структур данных. Это делается с помощью включения небольших файлов (файлов-заголовков), которые содержат определения типов и структуры данных, используемые в нескольких модулях. Файл заголовка в Си/С++ имеет обычно имя с расширением .H. Ассемблируемые модули обычно имеют файлы заголовков с расшире- нием .ASH (или .INC).
Программы, содержащие модули, написанные на Си/С++ и модули Турбо Ассемблера, должны иметь возможность совместного определе- ния типов и структур данных. Этому способствует утилита H2ASH.
Утилита H2ASH преобразует файлы заголовков Си/С++ в файлы заголовков языка Ассемблера. Как и исходные файлы с расширением .H полученные в результате файлы с расширением .ASH сами не со- держат программу. Если для вызова H2ASH вы используете следующий синтаксис:
H2ASH [[параметры] <имя_файла> [<имя_файла> .]]
где каждое поле "имя_файла" задает имя конвертируемого файла за- головка. Утилита H2ASH предполагает, что преобразуемый файл имеет по умолчанию расширение .H. Для каждого обрабатываемого файла со- ответствующий выходной файл имеет расширение .ASH.
"Параметры" соответствуют параметрам командной строки компи- лятора Borland C++, а также специфическим параметрам утилиты H2ASH (которые соответствуют параметрам -q Турбо Ассемблера). См. "Руководство пользователя Bоrland C++" и оперативный справочник, где можно найти описания данных параметров.
Утилита H2ASH преобразует следующие конструкции:
- все основные типы языка Си;
- типы ENUM (в типы ENUM TASM);
- #IFDEF (в эквивалентные IFDEF Ассемблера);
- #DEFINE (в определения EQU);
- структуры и объединения (в структуры и объединения Турбо Ассемблера);
- структуры с битовыми полями (в записи Ассемблера RECORD);
- определения TYPEDEF (в TYPEDEF Ассемблера);
- классы без множественного наследования и без виртуальных базовых классов;
- элементы классов;
- переопределения операций.
Утилита-преобразователь имеет следующие ограничения:

- весь преобразуемый код игнорируется, выполняемый код не генерируется;

- директивы #INCLUDE в выходных файлах полностью расширяются;

- в выводе Ассемблера может возникнуть конфликт имен, так как правила области действия С++ существенно отличаются от правил Ассемблера (помочь здесь может использование режима Ideal);

Преобразователь H2ASH не только облегчает организацию интер- фейса между С/С++ и языком Ассемблера (с максимальной производи- тельностью). H2ASH автоматически преобразует описания базовых классов С++ в эквивалентные объектные описания Ассемблера и опре- деляет "нескорректированные" имена методов. При записи базового класса на языке Ассемблера мы рекомендуем вам использовать следу- ющую процедуру:

1. Написать описание класса в файле заголовка языка Си.

2. Включить описание файла заголовка Си во все классы-потом- ки и модули на языке Си, которые используют этот базовый класс.

3. Используйте преобразователь H2ASH для файла заголовка с целью получения объектного файла заголовка Ассемблера (этот процесс можно автоматизировать с помощью формирую- щего файла).

4. Включите объектный файл заголовка Ассемблера в модуль, где записаны методы на языке Ассемблера.

Более подробную информацию об утилите H2ASH можно найти в текстовых файлах документации на дисках Турбо Ассемблера.

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

Value out of range

(Значение константы превышает допустимое значение)
Константа представляет собой, вообще говоря, число правиль- ного формата. Однако ее значение превышает допустимую в данном конкретном случае величину. Например:
DB 400

Ваша первая программа на Турбо Ассемблере

В программировании первой программой традиционно является программа, выводящая на экран сообщение "Привет!". Не будет иск- лючением и наша программа, поскольку это является хорошей отправ- ной точкой.
Войдите в текстовый редактор (один из тех редакторов, кото- рые формируют файлы в коде ASCII) и введите следующие строки программы под названием HELLO.ASM:
.MODEL SMALL .STACK 100h .DATA Message DB 'Привет!',13,10,'$' .CODE mov ax,@Data mov ds,ax ; установить регистр DS таким ; образом, чтобы он указывал ; на сегмент данных mov ah,9 ; функция DOS вывода строки mov dx,OFFSET Message ; ссылка на сообщение "Привет!" int 21h ; вывести "Привет!" на экран mov ah,4ch ; функция DOS завершения ; программы int 21h ; завершить программу END
После того, как вы введете эту программу, сохраните ее на диске.
Если вы знакомы с языками Си, С++ или Паскаль, вам может по- казаться, что эта версия программы несколько длинновата. Это действительно так, программы на Ассемблере длиннее, поскольку каждая инструкция Ассемблера выполняет меньше функций, чем инс- трукция Паскаля, С++ или языка Си. С другой стороны, вам предос- тавляется свобода, и вы можете комбинировать эти инструкции Ас- семблера так, как захотите. Это означает, что в отличие от языков Си и Паскаль, Ассемблер позволяет вам программировать компьютер таким образом, что он будет делать все, на что способен. Часто это стоит нескольких дополнительных строк.

Ваша вторая программа на Турбо Ассемблере

Теперь вы готовы к тому, чтобы ввести и запустить программу, которая действительно что-то делает. Вернитесь в текстовый редак- тор и введите следующую программу REVERSE.ASM:
.MODEL SMALL .STACK 100h .DATA MAXIMUM_STRING_LENGTH EQU 1000 StringToReverse DB MAXIMUM_STRING_LENGTH DUP (?) ReverseString DB MAXIMUM_STRING_LENGTH DUP (?) .CODE mov ax,@Data mov ds,ax ; установить регистр DS таким ; образом, чтобы он указывал mov ah,3fh ; функция DOS чтения ввода mov bx,0 ; описатель стандартного ввода mov cx,MAXIMUM_STRING_LENGTH ; считать до максималь- ; ного числа символов mov dx,OFFSET StringToReverse ; сохранить строку int 21h ; получить строку and ax,ax ; были считаны символы? jz Done ; нет, конец mov cx,ax ; поместить длину строки в ; регистр СХ, который можно ; использовать, как счетчик push cx ; сохранить в стеке длину ; строки mov bx,OFFSET StringToReverse mov si,OFFSET ReverseString add si,cx dec si ; указывает на конец буфера ; строки ReverseLoop: mov al,[bx] ; получить следующий символ mov [si],al ; сохранить символы в ; обратном порядке inc bx ; указатель на следующий ; символ dec si ; указатель на предыдущую ; ячейку buffer loop ReverseLoop ; переместить следующий ; символ, если он имеется pop cx ; извлечь длину строки mov ax,40h ; функция записи DOS mov bx,1 ; описатель стандартного ; вывода mov dx,OFFSET ReverceString ; напечатать строку Done: mov ah,4ch ; функция DOS завершения ; программы int 21h ; завершить программу END
Скоро вы увидите, что сможет делать эта программа. Для нача- ла не забудьте ее сохранить (под именем REVERSE.ASM).

Вещественные значения

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

Включение библиотеки

В том случае, когда вы знаете, что ваш исходный файл будет всегда использовать подпрограммы заданной библиотеки, можно ис- пользовать директиву INCLUDELIB. Директива INCLUDELIB сообщает компоновщику, что нужно включить указанную библиотеку. В режиме Ideal эта директива имеет следующий синтаксис:
INCLUDELIB "имя_файла" ; обратите внимание на кавычки!
а в режиме MASM:
INCLUDELIB имя_файла
где "имя_файла" - это имя библиотеки, которую вы хотите включать с помощью компоновщика на этапе компоновки. Если вы не укажете в заданном имени файла расширение, то компоновщик подразумевает расширение .LIB.
Приведем пример:
INCLUDELIB "diskio" ; включает DISKIO.LIB
Назад | Содержание | Вперед

Включение комментариев в тело макрокоманды

В особенно сложных макрокомандах иногда желательно поместить в теле макрокоманды комментарии, которые не должны включаться при ее расширении. Это также уменьшает объем необходимой Турбо Ас- семблеру для обработки макрокоманд памяти. Для этого используйте комментарий с предшествующей двойной точкой запятой (в начале строки). Например, следующее тело макрокоманды:
;; Это макрокоманда с комментарием DB 'макрокоманда с комментарием'
приведет к включению при ее вызове только следующего текста:
DB 'макрокоманда с комментарием'
Примечание: Комментарии с одиночной точкой с запятой при вызове макрокоманды всегда включаются в макрорасшире- ние.

Включение одной именованной структуры в другую

Чтобы способствовать наследованию объектов, Турбо Ассемблер обеспечивает способ, при котором весь тип данных структуры или объединения, включая имена элементов, встраивается в открытое оп- ределение структуры. Он интерпретирует встроенную структуру или объединение, как если бы они были вложены в этой точке внутри от- крытого определения структуры или объединения. Таким образом, встраивание одной структуры или объединения в другую принципиаль- но отличается от включения в структуру или объединение экземпляра другой структуры или объединения: экземпляр включает в себя толь- ко инициализированные или неинициализированные данные, в то время как встраиваемая структура или объединение включает в себя имена данных, структур и имена элементов.
Приведем синтаксис режима Ideal:
STRUC имя_структуры параметры_заполнения
В режиме MASM вы можете использовать следующий синтаксис:
имя_структуры STRUC параметры_заполнение
Оператор такого вида можно использовать только внутри струк- туры или объединения. Здесь "имя_структуры" - это имя ранее опре- деленной структуры или объединения, которая должны включаться. "Параметры_заполнения" задают изменения, которые вы хотите внести в начальные (используемые по умолчанию) значения включенных эле- ментов структуры. Ключевое слово ? указывает, что все встраивае- мые элементы структуры нужно рассматривать, как неинициализиро- ванные. В противном случае поле "параметры_заполнения" имеет следующий синтаксис:
{ [имя_элемента [=выражение] [,имя_элемента [=выражение].]] }
где "имя_элемента" - это имя любого элемента включаемой структу- ры, начальное значение которого при включении требуется изменить. "Выражение" представляет собой значение, на которое вы хотите его изменить. При наличии "выражения" начальное значение этого эле- мента структуры при его включении останется без изменения. Если вы задаете в поле выражения ключевое слово ?, то начальное значе- ние элемента будет записываться при его включении, как неинициа- лизированное.

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

Обычно, когда вы создаете экземпляр структуры, нужно обеспе- чить, что инициализированные данные содержал только один элемент объединения (подробности см. в Главе 12). Поскольку при встраива- нии структуры создание экземпляра не предусматривается, это огра- ничение здесь не применяется. Инициализированные данные могут со- держать несколько элементов включаемого объединения, например:

FOO STRUC ABC DW 1 DEF DW 2 UNION A1 DW '123' A2 DW ? ENDS ENDS

FOO2 STRUC FOO STRUC {A1=2} ; встроить структуру FOO в структуру ; FOO2 с переопределением ; заметим, что по умолчанию в FOO2 ; инициализируется и A1, и A2 GHI DB 3 ENDS

Определение структуры FOO2 в предыдущем примере эквивалентно следующей вложенной структуре/объединению:

FOO STRUC STRUC ; начало вложенной структуры. ABC DW 1 DEF DW 2 UNION ; начало вложенного объединения. A1 DW '123' A2 DW ? ENDS ; конец вложенного объединения. ENDS ; конец вложенной структуры. GHI DB 3 ENDS

Заметим, что при создании экземпляра FOO2 нужно убедиться, что инициализируется только одно значение в объединении.

Включение расширения макрокоманды в файл листинга

Расширения макрокоманд, состоящих из нескольких строк, обыч- но включаются в файл листинга. Однако в Турбо Ассемблере предус- мотрены следующей директивы:
- .LALL
- .SALL
- .XALL
- %MACS
- %NOMACS
которые позволяют управлять листингом макрорасширений.
Примечание: Подробнее об этих директивах рассказывает- ся в Главе 17.
Назад | Содержание | Вперед

Включение условных директив в файл листинга

Обычно код условного ассемблирования с ложным условием (false) в листинг не включается. Используя директивы Турбо Ас- семблера и параметры командной строки, вы можете это переопреде- лить.
Примечание: Более подробная информация содержится в Главе 2 и Главе 17.
Назад | Содержание | Вперед

Вложенные процедуры и правила области действия

Хотя вы можете вкладывать одну процедуру в другую, все про- цедуры имеют глобальную область действия, Например:
. . . test1 PROC FAR ; код процедуры CALL test2 ; код процедуры RET test2 PROC NEAR ; код процедуры RET ; ближний возврат test2 ENDP test1 ENDP . . .
В данном примере вне охватывающей процедуры можно вызывать test1 и test2.
Если вы хотите получить локальные подпроцедуры, используйте имя с локальной областью действия, например:
. . . LOCALS test1 PROC FAR ; код процедуры RET @test2 PROC NEAR ; код процедуры RET @test2 ENDP test1 ENDP . . .
Примечание: Директива LOCALS разрешает идентификаторы с локальной областью действия. Подробнее об этом рассказы- вается в Главе 11.
В данном коде в процедуре test1 вы можете обратиться только к процедуре @test2. Фактически, если они не находятся в одной и той же процедуре может существовать несколько процедур с именем @test2. Например, допустимо следующее:
. . . LOCALS test1 PROC FAR MOV si, OFFSET Buffer CALL @test2 RET @test2 PROC NEAR ; некоторый код RET @test2 ENDP
test2 PROC FAR MOV si,OFFSET Buffer2 CALL @test2 RET @test2 PROC NEAR ; некоторый код RET @test2 ENDP test2 ENDP . . .
Следующий код недопустим:
. . . lOCALS test1 PROC FAR MOV si,OFFSET Buffer CALL @test2 RET test1 ENDP
@test2 PROC NEAR ; код процедуры RET @test2 ENDP . . .
так как вызов @test2 задает локальный идентификатор для процедуры test1, а таких идентификаторов не существует.

Вложенные структуры и объединения

Турбо Ассемблер позволяется для управления смещениями, прис- ваевыми элементам структуры, использовать вложенные директивы STRUC, UNION и ENDS внутри открытого определения данных типа структуры или объединения.
В структуре каждый элемент данных начинается, когда заканчи- вается предыдущий элемент. В объединении каждый элемент данных начинается с того же смещения, что и предыдущий элемент данных. Если отдельный элемент данных может целиком включать в себя структуру или объединение, это дает существенную гибкость и отк- рывает большие возможности. Описание директив STRUC, UNION и ENDS можно найти в следующей таблице:
Директивы STRUC, UNION и ENDS Таблица 8.1 -----------------T----------------------------------------------¬ ¦ Директива ¦ Смысл ¦ +----------------+----------------------------------------------+ ¦ STRUC ¦ При использовании внутри открытой структуры ¦ ¦ ¦ или объединения данная директива начинает ¦ ¦ ¦ блок элементов, который внешняя (охватываю- ¦ ¦ ¦ щая) структура или объединение рассматрива- ¦ ¦ ¦ ет, как один элемент. Смещения элементам в ¦ ¦ ¦ блоке присваиваются в возрастающем порядке. ¦ ¦ ¦ Размер блока представляет собой сумму разме- ¦ ¦ ¦ ров всех элементов в нем. ¦ ¦ ¦ ¦ +----------------+----------------------------------------------+ ¦ UNION ¦ При использовании внутри открытой структуры ¦ ¦ ¦ или объединения данная директива начинает ¦ ¦ ¦ блок элементов, который охватывающая струк- ¦ ¦ ¦ тура или объединение рассматривает как один ¦ ¦ ¦ элемент. Элементам блока присваивается одно ¦ ¦ ¦ и то же смещение. Размер блока равен размеру ¦ ¦ ¦ наибольшего элемента в нем. ¦ ¦ ¦ ¦ +----------------+----------------------------------------------+ ¦ ENDS ¦ Завершает блок элементов, начинающихся пре- ¦ ¦ ¦ дыдущей директивой STRUC или UNION. ¦ ¦ ¦ ¦ L----------------+-----------------------------------------------
Например, в следующем определении данных типа структуры/объ- единения содержится в общей сложности 5 элементов.
CUNION STRUC CTYPE DB ? UNION ; начало объединения ; если CTYPE = 0, использовать следующее. STRUC CT0PAR1 DW 1 CT0PAR2 DW 1 ENDS ; если CTYPE = 1, использовать следующее. STRUC CT1PAR1 DW 3 CT1PAR2 DW 4 ENDS ENDS ; конец объединения ENDS ; конец структурного типа
Список этих элементов приведен в следующей таблице:
Элементы блока Таблица 8.1 ------------T---------T------------T----------------------------¬ ¦Элемент ¦ Тип ¦ Смещение ¦ Значение по умолчанию ¦ +-----------+---------+------------+----------------------------+ ¦ CTYPE ¦ Byte ¦ 0 ¦ ? (не инициализирован) ¦ ¦ CT0PAR1 ¦ Word ¦ 1 ¦ 1 ¦ ¦ CT0PAR2 ¦ Byte ¦ 3 ¦ 2 ¦ ¦ CT1PAR1 ¦ Byte ¦ 1 ¦ 3 ¦ ¦ CT1PAR2 ¦ Dword ¦ 2 ¦ 4 ¦ L-----------+---------+------------+-----------------------------
Длина данной структуры/объединения равна 6 байтам.

Внешние дальние идентификаторы

Если вы используете упрощенные директивы определения сегмен- тов, то описания идентификаторов EXTRN в сегментах дальнего типа не должны размещаться ни в каком сегменте, так как Турбо Ассем- блер рассматривает идентификаторы, описанные в данном сегменте, как связанные с данным сегментом. Это имеет свои недостатки: Тур- бо Ассемблер не может проверить возможность адресации к идентифи- катору, описанному, как внешний (EXTRN), вне любого сегмента и поэтому не может в случае необходимости сгенерировать определе- ние сегмента или сообщить вам, что была попытка обратиться к дан- ной переменной, когда сегмент не был загружен корректным значени- ем. Тем не менее Турбо Ассемблер генерирует для ссылок на такие внешние идентификаторы правильный код, но не может обеспечить обычную степень проверки возможности адресации к сегменту.
Если вы все-таки захотите, то можно использовать для явного описания каждого внешнего идентификатора сегмента старые директи- вы определения сегментов, а затем поместить директиву EXTRN для этого идентификатора внутрь описания сегмента. Это довольно уто- мительно, поэтому если вы не хотите обеспечивать загрузку коррек- тного значения сегмента при обращении к данным, то проще всего просто разместить описания EXTRN для идентификаторов дальнего типа вне всех сегментов. Предположим, например, что файл FILE1.ASM содержит следующее:
. . . .FARDATA FileVariable DB 0 . . .
и он компонуется с файлом FILE2.ASM, который содержит:
. . . .DATA EXTRN FileVariable:BYTE .CODE Start PROC mov ax,SEG FileVariable mov ds,ax . . .
SEG FileVariable не будет возвращать корректного значения сегмента. Директива EXTRN размещена в области действия директивы файла FILE2.ASM DATA, поэтому Турбо Ассемблер считает, что пере- менная FileVariable должна находиться в ближнем сегменте DATA файла FILE2.ASM, а не в дальнем сегмента DATA.
В следующем коде FILE2.ASM SEG FileVariable будет возвращать корректное значение сегмента:
. . . .DATA @CurSeg ENDS EXTRN FileVariable:BYTE .CODE Start PROC mov ax,SEG FileVariable mov ds,ax . . .
"Фокус" здесь состоит в том, что директива @CurSeg ENDS за- вершает сегмент .DATA, поэтому, когда переменная FileVariable описывается, как внешняя, никакая сегментная директива не дейс- твует.

Возможности фирмы Borland

Службы технического сопровождения фирмы Borland публикуют техническую информацию по множеству тем. Там вы можете получить ответы на возникшие вопросы.
TechFax (8000-822-4269) - это 24-часовая автоматизированная служба, позволяющая свободно получить техническую информацию по факсу. Вы можете запросить до трех документов за один вызов.
408-439-9096 (модем) - пересылка файлов (File DownLoad BBS, 2500 бод). Через модем вы можете получать образцы файлов, прик- ладных программ и техническую информацию. Специальной установки не требуется.
Подписчики информационных услуг CompuServe, GEnie или BIX могут получать техническую поддержку с помощью модема. Чтобы, имея доступ к информационной службе, связаться с Borland, исполь- зуйте следующую таблицу:
Оперативные информационные услуги ----------------------------------------- Служба Команда ----------------------------------------- CompuServe Go BORLAND BIX JOIN BORLAND GEnie BORLAND -----------------------------------------
Передачу электронных сообщений адресуйте All или Sysop. Се- рийный номер вам указывать не нужно: если сообщения не посылаются по частной системе пересылки, они общедоступны. Включите в вопрос максимум информации. Обслуживающий персонал ответит на сообщение в течении одного рабочего дня.
408-438-5300 - по этому телефону можно обратиться в наш от- дел технического обслуживания по рабочим дням с 6 утра до 5 вече- ра. Звоните пожалуйста по телефону, который находится недалеко от компьютера с работающей программой. Прежде чем позвонить, подго- товьте следующую информацию:
1. Название продукта и серийный номер вашей оригинальной дистрибутивной дискеты. Этот номер обязателен, иначе мы не сможем обслужить вашу заявку.
2. Марку и модель компьютера, а также любого используемого вами дополнительного аппаратного обеспечения.
3. Название и номер версии операционной системы. (Номер вер- сии можно определить, введя по приглашению DOS команду VER).
4. Содержимое вашего файла AUTOEXEC.BAT и содержимое файла CONFIG.SYS (в корневом каталоге (\) на загрузочном диске компьютера.
5. Телефон, по которому с вами можно связаться в рабочее время.
6. Если звонок связан с какой-то проблемой, какие шаги нужно выполнить, чтобы возникла эта проблема.
По телефону 408-438-5300 с 7 до 17 часов вы можете получить ответы на нетехнические вопросы, включая цены, изменения и т.д. (служба покупателя).
Содержание | Вперед

Возврат значений

Вызываемые из программы на языке С++ функции на Ассемблере, так же как и функции С++, могут возвращать значения. Значения функций возвращаются следующим образом:
---------------------------T------------------------------------¬ ¦Тип возвращаемого значения¦ Где находится возвращаемое значение¦ +--------------------------+------------------------------------+ ¦ unsigned char ¦ AX ¦ ¦ char ¦ AX ¦ ¦ enum ¦ AX ¦ ¦ unsigned short ¦ AX ¦ ¦ short ¦ AX ¦ ¦ unsigned int ¦ AX ¦ ¦ int ¦ AX ¦ ¦ unsigned long ¦ DX:AX ¦ ¦ long ¦ DX:AX ¦ ¦ float ¦ регистр вершины стека сопроцессора¦ ¦ ¦ 8087 (ST(0)) ¦ ¦ double ¦ регистр вершины стека сопроцессора¦ ¦ ¦ 8087 (ST(0)) ¦ ¦ long double ¦ регистр вершины стека сопроцессора¦ ¦ ¦ 8087 (ST(0)) ¦ ¦ near* ¦ AX ¦ ¦ far* ¦ DX:AX ¦ L--------------------------+-------------------------------------
В общем случае 8- и 16-битовые значения возвращаются в ре- гистре AX, а 32-битовые значения - в AX:DX (при этом старшие 16 бит значения находятся в регистре DX). Значения с плавающей точ- кой возвращаются в регистре ST(0), который представляет собой ре- гистр вершины стека сопроцессора 8087 или эмулятора сопроцессора 8087, если используется эмулятор операций с плавающей точкой.
Со структурами дело обстоит несколько сложнее. Структуры, имеющие длину 1 или 2 байта, возвращаются в регистре AX, а струк- туры длиной 4 байта - в регистрах AX:DX. Трехбайтовые структуры и структуры, превышающие 4 байта должны храниться в области стати- ческих данных, при этом должен возвращаться указатель на эти ста- тические данные. Как и все указатели, указатели на структуры, ко- торые имеют ближний тип (NEAR), возвращаются в регистре AX, а указатели дальнего типа - в паре регистров AX:DX.
Давайте рассмотрим вызываемую из программы на языке С++ функцию на Ассемблере с малой моделью памяти FindLastChar, кото- рая возвращает указатель на последний символ передаваемой строки. На языке С++ прототип этой функции выглядел бы следующим образом:
extern char * FindLastChar(char * StringToScan);

где StringToScan - это непустая строка, для которой должен возв- ращаться указатель на последний символ.

Функция FindLastChar имеет следующий вид:

.MODEL SMALL .CODE PUBLIC _FindLastChar _FindLastChar PROC push bp mov bp,sp cld ; в строковой инструкции нужно ; выполнять отсчет в прямом ; направлении mov ax,ds mov es,ax ; теперь ES указывает на ; ближний сегмент данных mov di, ; теперь ES:DI указывает на ; начало передаваемой строки mov al,0 ; найти нулевой символ, ; завершающий строку mov cx,0ffffh ; работать в пределах ; 64К-1 байт repne scasb ; найти нулевой символ dec di ; установить указатель ; обратно на 0 dec di ; ссылка обратно на ; последний символ mov ax,dx ; возвратить в AX указатель ; ближнего типа pop bp ret _FindLastChar ENDP END

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

Турбо Ассемблер фирмы Borland представляет

Турбо Ассемблер фирмы Borland представляет собой многопро- ходный ассемблер с разрешением опережающих ссылок, скоростью ас- семблирования до 48000 строк в минуту (на компьютере IBM PS/2, модель 60), совместимый с макроассемблером фирмы Microsoft MASM и дополнительной возможностью использования режима расширенного синтаксиса Ideal. Независимо от вашего опыта в программировании вы, несомненно, оцените эти особенности, а также ряд других средств, которые значительно облегчают программирование на Ас- семблере. Среди таких средств можно кратко упомянуть следующие (подробно они будут описаны позднее):
- возможности объектно-ориентированного программирования;
- 32-разрядная модель и поддержка кадра стека;
- поддержка интерфейса защищенного режима DOS (DPMI) для ра- боты Турбо Ассемблера в защищенном режиме под управлением Microsoft Windows;
- полная поддержка процессора 80386 и i486;
- упрощенные директивы определения сегментов;
- улучшенное управление листингом;
- поддержка таблиц;
- перечислимые типы;
- развитые инструкции работы с флагами;
- новые средства поддержки структур;
- быстрая операция умножения с непосредственным операндом;
- поддержка множественных определений;
- директива спецификации VERSION;
- режим Quirks для эмуляции MASM;
- полная отладка на уровне исходного текста с помощью Турбо отладчика;
- встроенная утилита генерации перекрестных ссылок (TCREF);
- файлы конфигурации и командные файлы;
- утилита-преобразователь файлов .h в .ash (включаемые фай- лы).
Турбо Ассемблер является мощным ассемблером, работающим с командной строкой, который воспринимает ваши исходные файлы (фай- лы с расширением .ASM) и создает из них объектные модули (файлы с расширением .OBJ). После этого вы можете использовать програм- му-компоновщик фирмы Borland TLINK.EXE, отличающуюся высокой ско- ростью компоновки, для компоновки полученных объектных модулей и создания выполняемых файлов (файлов с расширением .EXE).

Выделение пространства для локальных данных

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

Выделение статической памяти

Турбо Паскаль позволяет в программах Турбо Ассемблера резер- вировать пространство для статических переменных в сегментах гло- бальных данных (DATA или DSEG). Чтобы выделить это пространство, можно просто использовать такие директивы, как DB, DW и т.д. Нап- ример:
DATA SEGMENT PUBLIC MyInt DW ? ; зарезервировать слово MyByte DB ? ; зарезервировать байт . . . DATA ENDS
Переменных, выделяемых Турбо Ассемблером в сегменте глобаль- ных данных, касаются два важных ограничения. Во-первых, эти пере- менными являются "частными", они недоступны программе Турбо Пас- каля (хотя вы можете передавать указатели на них). Во-вторых, они не могут быть предварительно инициализированы, как типизованные константы. Оператор:
MyInt DW 42 ; это не инициализирует ; MyInt значением 42
не вызовет ошибки при компоновке модуля с программой Турбо Паска- ля, однако MyInt при выполнении программы не будет иметь значение 42.
Эти ограничения можно обойти, описав переменные или типизо- ванные константы Турбо Паскаля с помощью директивы EXTRN, что сделает их доступными Турбо Ассемблеру.

Выделение временной памяти

В ваших программах на Турбо Паскале можно выделять также временную память (локальные переменные) в стеке на время выполне- ния каждого вызова. Перед возвратом управления эта память должна быть освобождена, а значение регистра BP восстановлено. В следую- щем примере процедура MyProc резервирует пространство для двух целых переменных a и b:
CODE SEGMENT ASSUME CS:CODE MyProc PROC FAR ; procedure MyProc(i : integer); PUBLIC MyProc LOCAL a : WORD, b : WORD = LocalSpace ; a в [bp-2] ; b - в [bp-4] i equ word ptr [bp+6] ; параметр i находится над ; сохраненным BP и адресом ; возврата push bp ; нужно сохранить BP вызывающей ; программы mov bp,sp ; теперь BP указывает на ; вершину стека sub sp,LocalSpace ; зарезервировать пространст- ; во для двух слов mov ax,42 ; загрузить в AX начальное ; значение A mov a,ax ; и в A xor ax,ax ; очистить регистр AX mov b,ax ; инициализировать B нулем mov b,ax ; выполнить нужные действия . . . mov sp,bp ; восстановить исходное ; значение SP mov bp ; восстановить исходное ; значение регистра BP ret 2 MyProc ENDP CODE ENDS END
Примечание: Директива Турбо Ассемблера LOCAL использу- ется для создания идентификаторов и выделения пространства для локальных переменных.
Оператор:
LOCAL a : WORD, b : WORD = LocalSpace
на время выполнения процедуры присваивает идентификатору a значе- ние [BP-2], идентификатору b - значение [BP-4], а идентификатору LocalSpace - число 4 (размер области локальных переменных). Пос- кольку нет соответствующего оператора для создания идентификато- ров, ссылающихся на параметры, вы должны использовать присваива- ние i значения [BP+6].
Более разумный способ инициализации локальных переменных заключается в том, чтобы вместо уменьшения SP занести в стек их значения. Таким образом, вы должны заменить SUB SP,LocalSpace инструкциями:
mov ax,42 ; получить начальное значение ; для a push ax ; занести его в a xor ax,ax ; обнулить AX push ax ; и занести 0 в b
Если вы используете этот способ, нужно внимательно отслежи- вать стек! Не следует ссылаться на идентификаторы a и b перед тем, как они занесены в стек.
Другой вид оптимизации предусматривает использование инст- рукции PUSH CONST для инициализации локальных переменных (ее мож- но использовать при наличии процессором 80186, 80286 и 80386), или сохранение BP в регистре вместо занесения его в стек (если есть неиспользованные регистры).

Выполнение вызова

Все, что требуется от вас для передачи параметров в функцию C++, это занесение в стек самого правого параметра первым, затем следующего по порядку параметра и так далее, пока в стеке не ока- жется самый левый параметр. После этого нужно просто вызвать функцию. Например, при программировании на Borland C++ для вызова библиотечной функции Borland C++ strcpy для копирования строки SourceString в строку DestString можно ввести:
strcpy(DestString, SourceString);
Для выполнения того же вызова на Ассемблере нужно использо- вать инструкции:
lea ax,SourceString ; правый параметр push ax lea ax,DestString ; левый параметр push ax call _strcpy ; скопировать строку add sp,4 ; отбросить параметры
При настройке SP после вызова не забывайте очищать стек от параметров.
Можно упростить ваш код и сделать его независимым от языка, воспользовавшись расширением команды Турбо Ассемблера CALL:
call назначение [язык [,аргумент_1] .]
где "язык" - это C, PASCAL, BASIC, FORTRAN, PROLOG или NOLANGUAGE, а "аргумент_n" это любой допустимый аргумент програм- мы, который может быть прямо помещен в стек процессора.
Используя данное средство, можно записать:
lea ax,SourceString lea bx,DestString call strcpy c,bx,ax
Турбо Ассемблер автоматически вставит команды помещения ар- гументов в стек в последовательности, принятой в С++ (сначала AX, затем BX), выполнит вызов _strcopy (перед именами С++ Турбо Ас- семблер автоматически вставляет символ подчеркивания), и очищает стек после вызова.
Если вы вызываете функцию С++, которая использует соглашения Паскаля, заносите в стек параметры слева направо. После вызова настраивать указатель стека SP не требуется.
lea ax,DestString ; левый параметр push ax lea ax,SourceString ; правый параметр push ax call CTRCPY ; скопировать строку
Можно опять упростить ваш код, воспользовавшись расширением команды Турбо Ассемблера CALL:
lea bx,DestString ; самый левый параметр lea ax,SourceString ; самый правый параметр call strcpy pascal,bx,ax
Турбо Ассемблер автоматически вставит команды помещения ар- гументов в стек в последовательности, принятой в Паскале (сначала BX, затем AX), и выполнит вызов STRCPY (преобразуя имя к верхнему регистру, как принято в соглашениях Паскаля).
В последнем случае конечно подразумевается, что вы переком- пилировали функцию strcpy с параметром -p, так как в стандартной библиотечной версии данной функции используются соглашения по вы- зову, принятые в С++, а не в Паскале.
Функции С++ сохраняют следующие регистры (и только их): SI, DI, BP, DS, SS, SP и CS. Регистры AX, BX, CX, DX, ES и флаги мо- гут произвольно изменяться.

Выравнивание элементов структуры

В определения структуры для выравнивания элементов структур на соответствующие границы можно использовать директиву ALIGN. Например:
ALIGN 4 ; выравнивание на границу двойного слова member dd ? ; member будет выравниваться на DWORD

Выражения и операнды

Основное различие между выражениями режима Ideal и режима MASM состоит в функции, которую выполняют квадратные скобки. В режиме Ideal квадратные скобки всегда относятся к содержимому заключаемой в них величины. Квадратные скобки никогда не приводят в подразумеваемому сложению. Однако многие стандартные конструк- ции MASM в режиме Idealе не допускаются.
В режиме Ideal квадратные скобки должны использоваться для получения содержимого элемента. Например:
mov ax,wordptr
приводит к выводу предупреждающего сообщения, если вы пытаетесь загрузить указатель (wordptr) в регистр (AX). Корректной будет следующая форма:
mov ax,[wordptr]
Здесь ясно, что вы загружаете содержимое ячейки, адресуемой с помощью wordptr (в текущем сегменте данных DS) в регистр AX.
Если вы хотите сослаться на смещение идентификатора в сег- менте, то вы должны явно использовать операцию OFFSET, например:
mov ax,OFFSET wordptr

Выражения

Использование выражений позволяет вам получать модульный код, поскольку вы можете символически представлять в программе значения. Турбо Ассемблер выполняет все повторные вычисления, не- обходимые при изменениях (и не требует этого от вас).
В присваиваниях Турбо Ассемблер использует стандартное ин- фиксное обозначение. Выражения могут содержать операнды и унарные или бинарные операции. Унарная операция помещается перед одиноч- ным операндом; бинарные операции помещаются между двумя операнда- ми. Примеры простых операций показаны в Таблице 5.8.
Простые выражения Таблица 5.8 ---------------------T------------------------------------------¬ ¦ Выражение ¦ Что получается при вычислении ¦ +--------------------+------------------------------------------+ ¦ 5 ¦ константа 5 ¦ ¦ -5 ¦ константа -5 ¦ ¦ 4+3 ¦ константа 7 ¦ ¦ 4*3 ¦ константа 12 ¦ ¦ 4*3+2*1 ¦ константа 14 ¦ ¦ 4*(3+2)*1 ¦ константа 21 ¦ L--------------------+-------------------------------------------
В Приложении B содержится полная грамматика в форме Бэку- са-Наура, используемая при синтаксическом анализе выражений в режимах MASM и Ideal. Эта грамматика последовательно описывает синтаксис выражений Турбо Ассемблера, а также старшинство опера- ций.

Вывод информации на устройство печати

Устройство печати (принтер) - это очень полезное устройство. Вам может не только потребоваться распечатать текст программы, но и передать на принтер выводимую информацию. Следующая версия программы выводит информацию вместо экрана на принтер:
.MODEL SMALL .STACK 100h .DATA Message DB 'Привет!',13,10,'$' Message_Length EQO $ - Message .CODE mov ax,@Data mov ds,ax ; установить регистр DS таким ; образом, чтобы он указывал mov ah,40h ; функция DOS вывода строки ; на устройство mov bx,4 ; описатель принтера mov cx,Message_Length ; число печатаемых символов mov dx,OFFSET Message ; ссылка на "Привет!" int 21h ; вывести "Привет!" принтер mov ah,4ch ; функция DOS завершения ; программы int 21h ; завершить программу END
В данной версии программы функция DOS вывода строки на экран заменена на функцию DOS, которая передает информацию на выбранное устройство или в файл, в данном случае - на принтер. Введите и запустите программу. Посмотрите, как она напечатает на принтере слово "Привет!". (Перед запуском программы не забудьте ее сохра- нить при завершении работы в редакторе. При этом программа будет сохранена в файле HELLO.ASM, а предыдущая версия программы будет потеряна.)
Вы можете модифицировать эту программу таким образом, чтобы она снова посылала выходные данные на экран, а не на устройство печати, заменив просто строку:
mov bx,4 ; описатель принтера
на строку:
mov bx,1 ; описатель стандартного вывода
Сделайте такое изменение, а затем снова выполните перекомпи- ляцию и перекомпоновку программы перед ее запуском. Запустив программу, вы увидите, что сообщение появится на экране, а пос- ледним символом будет графический символ перевода формата (кружок с крестиком внизу - "зеркальце Венеры"). Этот символ программа передает на принтер, чтобы вынудить его после вывода сообщения выполнить перевод страницы, поскольку на экране страниц нет, он ничего не знает о переводе формата и просто выводит на экран сим- вол из набора символов компьютера РС.

Вывод нескольких сообщений об ошибках

По умолчанию Турбо Ассемблер допускает вывод для каждой строки исходного кода только одного сообщения об ошибке. Если строка исходного кода содержит несколько ошибок, то Турбо Ассемб- лер сообщает сначала о наиболее существенной из них. С помощью директив MULTERRS и NOMUTERRS вы можете управлять числом сообще- ний об ошибках, получаемых для каждой строки исходного кода.
Директива MULTERRS позволяет Ассемблеру сообщать для каждой строки исходного кода о более чем одно ошибке. Иногда это полезно использовать для обнаружения трудноуловимой ошибки, или когда ис- ходная строка содержит более одной ошибки.
Заметим, что иногда дополнительные сообщения об ошибках мо- гут вызвать "цепную реакцию", к которой приводит первая ошибка. После исправления первой ошибки в этом случае исчезает вся "це- почка" ошибок.
Приведем пример директивы MULTERRS:
MULTERRS mov ax,[bp+abc ; порождает две ошибки: ; 1) неопределенный идентификатор ; abc ; 2) нужна правая квадратная ; скобка
Директива NOMULTERRS позволяет выводить для каждой строки исходного кода только одно сообщение об ошибке или предупреждаю- щее сообщение. Когда вы исправите эту ошибку, могут исчезнуть также другие сообщения об ошибках. Однако если вы хотите опреде- лить вывод сообщений о самых серьезных ошибках, нужно использо- вать директиву MULTERRS, чтобы увидеть все сообщения об ошибках.
Приведем пример использования директивы NOMULTERRS:
NOMULTERRS mov ax,[bp+abc ; порождает одну ошибку: ; 1) неопределенный идентификатор ; abc
Назад | Содержание | Вперед

Вывод предупреждающих сообщений

Турбо Ассемблер предоставляет вам выбирать предупреждающие сообщения (если они имеются), которые будут выводиться в опреде- ленных частях кода. Каждое предупреждающее сообщение содержит трехбуквенный идентификатор, который вы можете заранее задать, чтобы Турбо Ассемблер знал, хотите или нет вы просматривать пре- дупреждающие сообщения этого класса. Для разрешения предупрежда- ющих сообщений вы можете использовать директиву WARN, а для их запрещения - NOWARN.
Синтаксис директивы WARN следующий:
WARN [класс_предупреждений]
где "класс_предупреждений" - это трехбуквенный идентификатор, за- дающий конкретный тип сообщения. Допускается использовать следую- щие классы предупреждений:
ALN Выравнивание сегмента BRK Требуются скобки ICG Неэффективная генерация кода LCO Переполнение счетчика инструкций OPI Открытие условного IF OPP Открыть процедуру OPS Открытие сегмента OVF Арифметическое переполнение PDC Конструкция, зависящая от прохода PRO Запись в память в защищенном режиме с исполь- зованием регистра CS PQK Предположение об использовании констант для [константа] предупреждение. RES Предупреждение о зарезервированном слове. TPI Недопустимое предупреждение Турбо Паскаля.
Заметим, что те же идентификаторы используются в параметре командной строки /W.
Приведем пример использования директивы WARN:
WARN OVF ; разрешается предупреждение о переполнении DW 1000-h * 123h ; выводится предупреждение о переполнении
Примечание: Директива WARN без класса предупреждений разрешает все предупреждения. Директива WARN с идентифика- тором разрешает только указанные предупреждения.
Для запрещения конкретного класса предупреждающих сообщений (или их всех) используйте директиву NOWARN. Эта директива исполь- зует те же идентификаторы, что и описанные выше для директивы WARN. Приведем примеры использования директивы NOWARN:
NOWARN OVF ; запрещается предупреждение о переполнении DW 1000-h * 123h ; не выводится предупреждение о ; переполнении

Вывод сообщения в процессе ассемблирования

Турбо Ассемблер обеспечивает две директивы, которые позволя- ют вам выводить строку на консоль в процессе ассемблирования: DISPLAY и %OUT. Вы можете использовать эти директивы для сообще- ний о ходе ассемблирования, о том, чтобы знать, насколько продви- нулся процесс ассемблирования, или чтобы определить, что достиг- нута определенная часть кода.
Эти две директивы практически представляют собой одно и то же, но директива DISPLAY выводит на экран строку, заключенную в кавычки, а %OUT - без кавычек.
В режимах Ideal и MASM директива DISPLAY имеет следующий синтаксис:
DISPLAY "текст"
где "текст" - любое сообщение, которое вы хотите вывести на эк- ран. Заметим, что для вывода информации вместо экрана в файл вы можете использовать средство изменения направления вывода DOS.
Директива %OUT в режимах Ideal и MASM имеет следующий син- таксис:
%OUT текст
где "текст" также представляет то, что вы хотите вывести.

Вызов Borland C++ из Турбо Ассемблера

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

Вызов функции Турбо Ассемблера из Borland C++

Теперь мы рассмотрим пример программы на Borland C++, вызы- вающей функцию Турбо Ассемблера. Модуль Турбо Ассемблера COUNT.ASM содержит функцию LineCount, которая возвращает значение счетчика числа строк и символов в передаваемой строке:
; Вызываемая из С++ функция на Ассемблере с малой моделью памяти ; для подсчета числа строк и символов в завершающейся нулем ; "строке". ; ; Прототип функции: ; extern unsigned int LineCount(char * near StringToCount, ; unsigned int near * CharacterCountPtr); ; ; Ввод: ; char near * StringToCount: указатель на "строку", в ; которой нужно выполнить подсчет строк. ; ; unsigned int near * CharacterCountPtr: указатель на ; целую переменную, в которую нужно записать значение ; счетчика NEWLINE EQU 0ah ; символ перевода строки в Си .MODEL SMALL .CODE PUBLIC _LineCount __LineCount PROC push bp mov bp,sp push si ; сохранить регистровую ; переменную вызывающей ; программы mov si,[bp+4] ; SI указывает на строку sub cx,cx ; установить значение ; счетчика символов в 0 mov dx,cx ; установить в 0 счетчик ; строк LineCountLoop: lodsb ; получить следующий символ and al,al ; это 0? конец строки? jz EndLineCount ; да, выполнено inc cx ; нет, подсчитать следующий ; символ cmp al,NEWLINE ; это новая строка? jnz LineCountLoop ; нет, проверить ; следующий символ inc dx ; да, подсчитать еще одну ; строку jmp LineCountLoop EndLineCount: inc dx ; подсчитать строку, которая ; завершается нулевым символом mov [bx],cx ; задать значение переменной- ; счетчика mov ax,dx ; возвратить счетчик строк в ; качестве значения счетчика pop si ; восстановить регистровую ; переменную вызывающей ; программы pop bp ret _LineCount ENDP END
Следующий модуль на языке С++ с именем CALLCT.CPP представ- ляет собой пример вызова функции LineCount:
char * TestString="Line 1\nline 2\nline 3"; extern "C" { unsigned int LineCount(char * StringToCount, unsigned int near * CharacterCountPtr); } main() { unsigned int LCount; unsigned int CCount;

Lcount = LineCount(TestString, &CCount); printf("Lines: %d\nCharacters: %d\n", LCount, CCount); }

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

bcc -ms callct.cpp count.asm

Как здесь показано, функция LineCount будет работать только при компоновке с программами на языке С++, в которых используется малая модель памяти, так как в других моделях размеры указателей и адресов в стеке изменятся. Приведем пример версии функции LineCount (COUNTLG.ASM), которая будет работать с программами на С++, использующим большую модель памяти (но не малую модель: пос- кольку передаются дальние указатель, функция LineCount также опи- сана, как функция дальнего типа):

; Вызываемая из С++ функция на Ассемблере для подсчета числа ; строк и символов в завершающейся нулем "строке". ; ; Прототип функции: ; extern unsigned int LineCount(char * far StringToCount, ; unsigned int far * CharacterCountPtr); ; ; Ввод: ; char far * StringToCount: указатель на "строку", в ; которой нужно выполнить подсчет строк. ; ; unsigned int far * CharacterCountPtr: указатель на ; целочисленную переменную, в которую нужно записать ; значение счетчика NEWLINE EQU 0ah ; символ перевода строки в Си .MODEL LARGE .CODE PUBLIC _LinaCount _LineCount PROC push bp mov bp,sp push si ; сохранить регистровую ; переменную вызывающей ; программы push ds ; сохранить стандартный ; сегмент данных lds si,[bp+6] ; DS:SI указывает на строку sub cx,cx ; установить значение ; счетчика символов в 0 mov dx,cx ; установить в 0 счетчик ; строк LineCountLoop: lodsb ; получить следующий символ and al,al ; это 0? конец строки? jz EndLineCount ; да, выполнено inc cx ; нет, подсчитать следующий ; символ cmp al,NEWLINE ; это новая строка? jnz LineCountLoop ; нет, проверить ; следующий символ inc dx ; да, подсчитать еще одну ; строку jmp LineCountLoop EndLineCount: inc dx ; подсчитать строку, которая ; завершается нулевым символом les bx,[bp+10] ; ES:BX указывает на ячейку, ; в которой возвращается ; значение счетчика mov es:[bx],cx ; задать значение переменной- ; счетчика mov ax,dx ; возвратить счетчик строк в ; качестве значения счетчика pop ds ; восстановить стандартный ; сегмент данных Си pop si ; восстановить регистровую ; переменную вызывающей ; программы pop bp ret _LineCount ENDP END

Программу COUNTLG.ASM можно скомпоновать с CALLCT.CPP с по- мощью следующей командной строки:

bcc -ml callct.cpp countlg.asm

Вызов из Borland C++ функций Турбо Ассемблера

Обычно C++ и Ассемблер совместно используют путем написания отдельных модулей целиком на C++ или Ассемблере, компиляции моду- лей С++ и ассемблирования модулей Ассемблера с последующей сов- местно компоновкой этих раздельно написанных модулей. Это пока- зано на Рис. 1.18.
-------------------------------¬ -------------------------------¬ ¦ Исходный файл на языке С++ ¦ ¦ Исходный файл на Ассемблере ¦ ¦ имя_файла.СPP ¦ ¦ имя_файла.ASM ¦ L--------------T---------------- L------------T------------------ ¦ ¦ Компиляция Ассемблирование -=============¬ -==================¬ ¦ Borland C++ ¦ ¦ Турбо Ассемблер ¦ L=============- L==================- ¦ ¦ -------------------------------¬ -------------------------------¬ ¦ Объектный файл языка С++ ¦ ¦ Объектный файл языка С++ ¦ ¦ имя_файла.OBJ ¦ ¦ имя_файла.OBJ ¦ L--------------T---------------- L-------------T----------------- ¦ ¦ ¦ ¦ L-------------¬ --------------- ¦ ¦ -=============¬ ¦ TLINK ¦ Компоновка L=============- ¦ ---------------------------------¬ ¦ Выполняемый файл ¦ ¦ имя_файла.EXE ¦ L---------------------------------
Рис. 1.18 Цикл компиляции, ассемблирования и компоновки Borland C++, Турбо Ассемблера и компоновщика TLINK
Выполняемый файл получается из "смеси" модулей С++ и Ассемб- лера. Этот цикл можно запустить командой:
bcc имя_файла_1.cpp имя_файла_2.asm
которая указывает Borland C++, что нужно сначала компилировать файл имя_файла_1.СPP в файл имя_файла_1.OBJ, а затем вызвать Тур- бо Ассемблер для ассемблирования файла имя_файла_2.asm в имя_фай- ла_2.obj, и, наконец, вызвать компоновщик TLINK для компоновки файла имя_файл_1.OBJ и имя_файл_2.OBJ в файл имя_файла.EXE.
Раздельную компиляцию полезно использовать для программ с большим объемом кода на Ассемблере, так как это позволяет исполь- зовать все возможности Турбо Ассемблера и программировать на язы- ке Ассемблера в чисто ассемблерном окружении без ключевых слов asm, дополнительного времени на компиляцию и связанными с С++ непроизводительными затратами при работе со встроенным Ассембле- ром.
За раздельную компиляцию приходится платить следующую цену: программист, работающий с Ассемблером, должен вникать во все де- тали организации интерфейса между С++ и кодом Ассемблера. В то время как при использовании встроенного Ассемблера Borland C++ сам выполняет спецификацию сегментов, передачу параметров, ссылку на переменные С++ и т.д., отдельно компилируемые функции Ассемб- лера должны все это (и даже более) делать самостоятельно.

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

Давайте теперь приступим к изучению правил компоновки прог- рамм Турбо Ассемблера и Borland C++.

Вызов из Турбо Ассемблера функции Borland C++

Одним из случаев, когда вам может потребоваться вызвать из Турбо Ассемблера функцию Borland C++, является необходимость вы- полнения сложных вычислений, поскольку вычисления гораздо проще выполнять на С++, чем на Ассемблера. Особенно это относится к случаю смешанных вычислений, где используются и значения с плава- ющей точкой и целые числа. Лучше возложить функции по выполнению преобразования типов и реализации арифметики с плавающей точкой на С++.
Давайте рассмотрим пример программы на Ассемблере, которая вызывает функцию Borland C++, чтобы выполнить вычисления с плава- ющей точкой. Фактически в данном примере функция Borland C++ пе- редает последовательность целых чисел другой функции Турбо Ас- семблера, которая суммирует числа и в свою очередь вызывает другую функцию Borland C++ для выполнения вычислений с плавающей точкой (вычисление среднего значения).
Часть программы CALCAVG.CPP, реализованная на С++ (CALCAVG.CPP), выглядит следующим образом:
#include extern float Average(int far * ValuePtr, int NumberOfValues); #define NUMBER_OF_TEST_VALUES 10 int TestValues(NUMBER_OF_TEST_VALUES) = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
main() { printf("Среднее арифметическое равно: %f\n", Average(TestValues, NUMBER_OF_TEST_VALUES)); } float IntDivide(int Divedent, int Divisor) } return( (float) Divident / (float) Divisor );
}
а часть программы на Ассемблере (AVERAGE.ASM) имеет вид:
; ; Вызываемая из С++ функция с малой моделью памяти, ; которая возвращает среднее арифметическое последова- ; тельности целых чисел. Для выполнения завершающего ; деления вызывает функцию С++ IntDivide(). ; ; Прототип функции: ; extern float Average(int far * ValuePtr, ; int NumberOfValues); ; ; Ввод: ; int far * ValuePtr: ; массив значений для ; ; вычисления среднего ; int NumberOfValues: ; число значений для ; ; вычисления среднего .MODEL SMALL EXTRN _IntDivide:PROC .CODE PUBLIC _Average _Average PROC push bp mov bp,sp les bx,[bp+4] ; ES:BX указывает на ; массив значений mov cx,[bp+8] ; число значений, для ; которых нужно ; вычислить среднее mov ax,0 AverageLoop: add ax,es:[bx] ; прибавить текущее ; значение add ax,2 ; ссылка на следующее ; значение loop AverageLoop push WORD PTR [bp+8] ; получить снова число ; значений, переданных ; в функцию IntDivide ; в правом параметре push ax ; передать сумму в ; левом параметре call _IntDivide ; вычислить среднее ; значение с плавающей ; точкой add sp,4 ; отбросить параметры pop bp ret ; среднее значение в ; регистре вершины ; стека сопроцессора ; 8087
_Average ENDP END

Основная функция (main) на языке С++ передает указатель на массив целых чисел TestValues и длину массива в функцию на Ас- семблере Average. Эта функция вычисляет сумму целых чисел, а за- тем передает эту сумму и число значений в функцию С++ IntDivide. Функция IntDivide приводит сумму и число значений к типу с плава- ющей точкой и вычисляет среднее значение (делая это с помощью од- ной строки на С++, в то время как на Ассемблере для этого потре- бовалось бы несколько строк). Функция IntDivide возвращает сред- нее значение (Average) в регистре вершины стека сопроцессора 8087 и передает управление обратно основной функции.

Программы CALCAVG.CPP и AVERAGE.ASM можно скомпилировать и скомпоновать в выполняемую программу CALCAVG.EXE с помощью коман- ды:

bcc calcavg.cpp average.asm

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

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

.MODEL small,C EXTRN C IntDivide:PROC .CODE PUBLIC C Average Average PROC C ValuePtr:DWORD, NumberOfValues:WORD les bx,ValuePtr mov cx,NumberOfValues mov ax,0 AverageLoop: add ax,es:[bx] add bx,2 ;установить указатель ;на следующее значение loop AverageLoop call _IntDivide C,ax,NumberOfValues ret Average ENDP END

Вызов метода объекта

Для вызова метода объекта используйте инструкцию CALL. Для вызова процедур методов Турбо Ассемблер обеспечивает расширение стандартной инструкции CALL - CALL.METHOD.
Примечание: Синтаксис инструкции CALL для вызова ста- тических и виртуальных методов совпадает.

Вызов общей макрокоманды, состоящей из нескольких строк

Чтобы вызвать общую макрокоманду, используйте в вашей прог- раммы в качестве директивы имя макрокоманды. Турбо Ассемблер вставляет в этой точке модуля (поле подстановок всех формальных аргументов) тело макрокоманды. При вызове общей макрокоманды, состоящей из нескольких строк, используется следующий синтаксис:
имя_макрокоманды [аргумент [[,]аргументе].]
где "имя_макрокоманды" - это идентификатор макрокоманды. Если вы вызываете макрокоманду с аргументами, то список аргументов указы- вается после имени макрокоманды. Вы можете задать любое число аргументов, но все они должны умещаться на одной строке. Аргумен- ты нужно отделять друг от друга запятыми или пробелами. При рас- ширении макрокоманды Турбо Ассемблер заменяет первый формальный аргумент макроопределения первым передаваемым аргументом, второй аргумент - вторым аргументом и т.д.
Каждый "аргумент" представляет текстовую строку. Вы можете задать эту текстовую строку следующими способами:
- как непрерывную группу символов, не содержащую пробелов, запятых или двоеточий;
- как группу символов, выделенную угловыми скобками (<>), которая может содержать пробелы, запятые и двоеточия;
- как одиночный символ с предшествующим знаком !, что экви- валентно заключению символа в угловые скобки;
- как выражение с предшествующим символом %, которое предс- тавляет текстовое значение выражения, соответствующее те- кущему выбранному основанию.

Вызов процедур методов для объектов: CALL.METHOD

Инструкция CALL расширена таким образом, чтобы поддерживать методы объектов. Вызов метода объекта может генерировать либо не- посредственный вызов (для статических методов), либо косвенный вызов (для виртуальных методов).
Поскольку вы можете использовать косвенный вызов, выполняю- щие вызов инструкции могут нарушить содержимое некоторых регист- ров. В этом случае, если вы используете вызов виртуального мето- да, Турбо Ассемблер позволяет вам выбрать соответствующие регист- ры. Приведем синтаксис расширения CALL.METHOD: CALL указатель_экземпляра METHOD [имя_объекта:]имя_метода [USES [сегм_регистр:]регистр_смещения] [язык_и_аргументы]
где "указатель_экземпляра" должен описывать экземпляр объекта. В режиме MASM часто невозможно определить имя объекта, связанного с экземпляром. В этом случае Турбо Ассемблер позволяет использовать поле "имя_объекта", благодаря чему вы можете задать имя экземпля- ра объекта.
Поле "имя_метода" содержит имя метода, который должен вызы- ваться для конкретного экземпляра объекта.
Примечание: О том, как задавать виртуальных или стати- ческий метод, подробнее рассказывается в Главе 8.
Если метод является виртуальным,и требуется косвенный вызов, инструкция CALL.METHOD выполняет косвенный вызов через регистры ES:BX (ES:EBX для модели USE32 процессора 80386). Если вы хотите использовать другие регистры, можно переопределить их с помощью оператора USES. Поле "сегм_регистр" это необязательный используе- мый сегментный регистр, а "регистр_смещения" - это регистр смеще- ния, используемый для вызова.
Для объектов, описанных с помощью таблиц NEAR, CALL.METHOD загружает только регистр смещения. Турбо Ассемблер предполагает, что сегментный регистр всегда установлен в корректное значение.
Примечание: Хорошей практикой программирования явля- ется соответствующий вызов методов с помощью косвенного вызова, даже когда вы знаете, что вызываемый метод являет- ся статическим. При модификации объектов методы могут из- мениться со статических на виртуальные.
Поле "язык_и_аргументы" инструкции CALL.METHOD содержит необязательные спецификации языка и аргументов, которые идентичны по форме описанным в предыдущем разделе.
Вызов процедур методов для С++ и Паскаля требует обычно, чтобы экземпляр объекта передавался в качестве аргумента в стеке. Подробнее об этом рассказывается в Главе 18 и Главе 19.

Вызов процедур с кадрами стека

Турбо Ассемблер поддерживает расширенную форму инструкции CALL, которая позволяет вам непосредственно вызывать процедуры, использующие интерфейсные соглашения языков высокого уровня.
Аргументы процедурам, использующим интерфейсные соглашения языков высокого уровня, передаются через стек в кадре стека. Пе- ред вызовом процедуры вызывающая программа должна занести эти ар- гументы в стек.
Используемые процедурой интерфейсные соглашения определяют порядок, в котором аргументы должны заноситься в стек. Для проце- дур Бейсика, Фортрана и Паскаля аргументы заносятся в стек в том порядке, в котором они указываются. Для языков Си и С++ аргументы заносятся в стек в обратном порядке.
Используемые процедурой интерфейсные соглашения определяют также, должна удалять аргументы из стека после вызова процедуры сама процедура или вызывающая программа. Языка Си и С++ требуют, чтобы стек очищала вызывающая программа. Во всех других языках процедура должна сама удалить аргументы из стека перед возвратом управления.
Турбо Ассемблер с помощью расширенной инструкции CALL сам выполняет за вас функции по занесению аргументов в стек в нужном порядке и очисткой стека. Вызов процедуры с параметрами имеет следующий синтаксис:
CALL выражение [язык] [,список_аргументов]
где "выражение" является целью инструкции CALL, "язык" задает ис- пользуемые при вызове языковые соглашения. Если вы не задаете язык, Турбо Ассемблер использует по умолчанию язык, заданный в директиве MODEL.
Примечание: О директиве MODEL более подробнее расска- зывается в Главе 7.
Аргументы, если они имеются, указываются после идентификато- ра языка. Синтаксис каждого аргумента в списке совпадает с син- таксисом, который используется в расширенных инструкциях PUSH и POP. Вы можете разделить эти аргументы запятыми, например:
CALL test PASCAL,ax,es OFFSET buffer,blen
Поскольку в данном примере указан Паскаль, Турбо Ассемблер будет заносить аргументы в стек в том порядке, в котором они ука- заны. Этот пример эквивалентен следующим инструкциям:

PUSH ax PUSH es OFFSET buffer PUSH word PTR blen CALL test

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

CALL test C,ax,es OFFSET buffer, word PTR blen

даст в результате следующий код:

PUSH word PTR blen PUSH es OFFSET buffer PUSH ax CALL test SUB sp,8

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

Нужно не забывать разделять аргументы запятыми, а компоненты аргументов - пробелами. В зависимости от соглашений указанного языка Турбо Ассемблер может заносить аргументы в стек в обратном порядке, но порядок компонентов аргумента он не изменяет.

Если при вызове используются интерфейсные соглашения NOLANGUAGE, Турбо Ассемблер при наличии любого аргумента сообщает об ошибке. Хотя аргументы для процедуры NOLANGUAGE можно задать с помощью директивы ARG, при вызове такой процедуры требуется явное занесение аргументов в стек.

Вызов статического метода

При вызове процедуры метода, даже если вы знаете, что вызы- вается статический метод, следует записывать инструкцию CALL.METHOD, как если бы вы вызывали виртуальный метод. Это позволит при вызове статических методов избежать нежелательных эффектов и предоставляет вам гибкость - возможность изменять ста- тические методы на виртуальные и обратно без необходимости измене- ния вызова метода. По этой же причине имеет смысл выбирать проме- жуточные регистры вызова, даже если вы знаете, что вызываемый вами метод является статическим.
Вызовы статических методов определяются на этапе компиляции и преобразуются в непосредственный вызов нужной процедуры метода объекта. Однако при выполнении вызова не следует вызывать проце- дуру метода непосредственно. Вместо этого используйте расширенную инструкцию CALL.METHOD.
В следующей таблице показан пример вызова статического мето- да init для объекта связанного списка:
CALL foolist METHOD list:init pascal,ds offset foolist CALL es:di METHOD list:init pascal,es di
Сам адрес вызова является адресом экземпляра объекта. Этот адрес используется только по синтаксическим причинам. Фактически генерируемым адресом является непосредственный вызов процедуры метода.
В данном примере первым вызовом является вызов метода init объекта list. Так как это статический метод, вы выполняете непос- редственный вызов процедуры метода list_init. Турбо Ассемблер иг- норирует экземпляр объекта foolist (он только передается в ка- честве аргумента процедуре метода).
За именем вызова следует обычный расширенный параметр языка и список параметров. Язык и параметры зависят от вызываемого вами метода. Один из параметров обычно является указателем на экземп- ляр объекта. В данном примере метод воспринимает один параметр, являющийся указателем на экземпляр объекта.

Вызов виртуального метода

Любой вызов виртуального метода требует косвенного вызова процедуры метода. Для этого используйте расширенную инструкцию CALL.METHOD. Для выполнения вызова Турбо Ассемблер генерирует следующие инструкции:
1. Загружает промежуточные регистры указателем на ТВМ из эк- земпляра объекта.
2. Выполняет косвенный вызов соответствующего элемента таб- лицы.
Таким образом, когда вы задаете:
CALL <экземпляр> METHOD <объект>:<метод> USES : <вызов_проц>
то генерируются следующие инструкции:
MOV <рег>, [<экземпляр>.<указатель_ТВМ>] CALL [(<сегм><рег>).<метод>] <вызыв_проц>
Первая инструкция загружает выбранный регистр <рег> адресом таблицы виртуальных методов из поля указателя ТВМ структуры объ- екта. Вторая инструкция выполняет косвенный вызов соответствующе- го метода в таблице.
Например, вызов в виде:
CALL es:di method list:insert uses ds:bx pascal,es di,es dx,es cx
генерирует последовательность вида:
mov bx,[es:di.@Mptr_list] CALL [ds:bx.insert] pascal,es di,es dx,es cx
Заметим, для объектов, описанных с таблицами NEAR, инструк- цией CALL.METHOD будет загружаться только регистр смещения. Сегментный регистр всегда должен содержать корректное значение. В следующем примере показано, как обеспечить правильную установку сегментного регистра:
; Добавить узел к концу объекта связанного списка. ; Это виртуальный метод "list_append". list_append PROC PASCAL NEAR ARG @@list:dword,\ @@new:dword USES dx,bx, es,di mov ax,@Data mov ds,ax les di,@@list sub ax,ax CALL es:di method list:insert uses DS:bx pascal, es di,@@new,ax ax ret ENDP
Примечание: Пока вы не инициализируете в данных объекта указатель таблицы виртуальных методов, ни один виртуальный метод вызвать нельзя. Это вызвано тем, что указатель загру- жает адрес ТВМ (из которой извлекается адрес нужной проце- дуры виртуального метода). Таким образом, если вы не иници- ализировали указатель на таблицу виртуальных методов, любой вызов виртуального метода приведет к вызову по некоторому случайному адресу.

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

node STRUC GLOBAL METHOD { construct:dword = node_construct ; подпрограмма ; конструктора узла destroy:dword = node_destroy ; подпрограмма ; деструктора узла init:dword = node_init ; подпрограмма ; инициализации узла deinit:dword = node_deinit ; подпрограмма ; деинициализации узла routine virtual next:word = node_adv ; подпрограмма ; следующего узла virtual prev:word = node_back ; подпрограмма ; предыдущего узла virtual print:word = node_print ; подпрограмма ; содержимого узла } node_next dd ? ; указатель следующего ; узла node_prev dd ? ; указатель ; предыдущего узла ends

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

mlabel STRUC GLOBAL node METHOD { virtual print:word = label_print } label_name db 80 dup (?) label_addr db 80*2 dup (?) label_city db 80 dup (?) label_state db 2 dup (?) label_zip db 10 dup (?) ENDS

book STRUC GLOBAL node METHOD { virtual print:word = book_print } book_title db 80 dup (?) book_author db 80 dup (?) ENDS

В следующем примере вы для объектов label и book вызываем методы путем вызова printit. Если "предком" является node, не важно, какой объект передается printit. Так как метод печати - это виртуальный метод, вызов выполняется косвенно через ТВМ объ- екта. При первом вызове printit, так как мы передаем экземпляр объекта label, вызывается процедура метода label_print. При вто- ром вызове printit вызывается процедура метода book_print, пос- кольку мы передаем экземпляр объекта book. Заметим, что если бы метод print был статическим, то при вызове node_print всегда вы- зывалась бы процедура node_print (что нежелательно).

call printit pascal,<<адрес экземпляра объекта label>> call printit pascal,<<адрес экземпляра объекта book>> . . . printit proc pascal near arg @@obj:dword uses ds,si,es,bx mov ax,@data mov es,ax lds si@@obj call ds:si method node:print uses es:bx pascal,ds si ret endp

Вызов виртуальных методов "предков"

Благодаря тому, что вы повторно сможете использовать исход- ный код, применение виртуальных методов "предков" может помочь вам записывать методы порожденных классов. Например, пока вы не зададите, является элемент очередью или списком, для очереди мож- но использовать те же методы вывода, что и для списка. В классе списка вы можете записать:
virtual show:word = list_show
а в классе очереди:
virtual show:word = queue_show
Подпрограмма list_show может печатать LIST SHOW: с последую- щим выводом отдельных элементов списка. Однако в порожденном классе, если queue_show использует подпрограмму печати, она долж- на печатать собственный заголовок QUEUE SHOW: и использовать list _show только как механизм последовательного прохода по списку и печати отдельных элементов. list_show может определить передавае- мый ей тип структуры и в зависимости от этого печатать заголовок списка. Если подпрограмма для list_show посмотрит на указатель таблицы виртуальных методов передаваемой ей структуры, она сможет определить, совпадает ли указатель с указателем, установленным в подпрограмме list_init для списков (или они различны). Если ука- затель ТВМ в структуре не указывает на таблицу виртуальных мето- дов для списков, то вероятно структура является порожденным ти- пом. list_show может выполнить эту проверку с помощью следующих операторов:
cmp [([es:di]).@mptr_list],offset @TableAddr_LIST jne @@not_a_list ; пропустить печать заголовка списка
; Если мы попали сюда, то это список, и следует ; распечатать его заголовок. . @@not_a_list: ; Теперь вывести отдельные элементы списка.
Как можно вызвать класс списка и метод вывода из подпрограм- мы queue_show? Если бы вы вызвали list_show непосредственно, то в подпрограмме могла бы возникнуть проблема, если имя используемого для вывода метода изменилось. (Вы можете не помнить об изменениях в вызове queue_show.) Если в queue_show вы поместите следующий оператор:
call(es:di) method list:show
то получите бесконечный цикл, поскольку, хотя список задан как класс, для которого вызывается метод вывода, так как метод вывода является виртуальным, будет использоваться ТВМ. Поскольку ТВМ для структуры указывала бы на queue_show, вы вернулись бы к той же подпрограмме.

Наилучшим способом вызова метода класса является следующий:

call +@table_list | show

Поскольку при описании класса list_show было задано как зна- чение элемента вывода @table_list, Турбо Ассемблер автоматически транслирует данный оператор в непосредственный вызов list_show. Заметим, что хотя в списке метод вывода описывается как виртуаль- ный, задание вызова приводит к тому, что Турбо Ассемблер выполня- ет непосредственный вызов без просмотра ТВМ.

Примечание: Виртуальные подпрограммы обычно вызываются косвенным образом через просмотр ТВМ.

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

mov bx,offset @TABLEADDR_LIST call [(@table_list ptr es:bx).SHOW]

Это аналогично последовательности инструкций, которые Турбо Ассемблер использует для выполнения косвенного вызова через ТВМ.

Вызываемые процедуры, содержащие RETURNS

Процедуры, определяющие некоторые из своих аргументов с по- мощью ключевого слова RETURNS требуют отдельного рассмотрения. Эти аргументы используются для возврата значений в вызывающую программу. Таким образом, вызывающая программа всегда извлекает их из стека. Чтобы способствовать передаче этих аргументов, зад- анных в описании процедуры после директивы RETURNS, в Турбо Ас- семблере нет специальных расширений инструкции CALL. Перед инс- трукцией CALL вы должны явным образом занести аргументы в стек (PUSH), а потом извлечь их их стека (POP).

Взаимодействие между Турбо Ассемблером и Borland C++

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

Если текущим выбранным процессором является

Если текущим выбранным процессором является процессор 80386 или старше, то Турбо Ассемблер предоставляет две операции, кото- рые определяют, будет выражение интерпретироваться как 16- или 32 -разрядное значение. Это операции SMALL и LARGE. Они имеют сле- дующий синтаксис:
SMALL выражение LARGE выражение
Операция SMALL помечает выражение, как представляющее 16-разрядное значение. Операция LARGE помечает его как 32-разряд- ное значение. Эти операции особенно важны в программах, при рабо- те которой в операционной среде некоторые сегменты являются 32-битовыми, а другие - 16-битовыми. Например, инструкция:
JMP [DWORD PTR ABC]
представляет собой косвенный переход на содержимое переменной в памяти ABC. Если у вас разрешено использование процессора 80386, то данная инструкция может интерпретироваться либо как дальний переход с использованием сегмента и 16-разрядного смещения, либо как ближний переход по 32-разрядному смещению. Для устранения неоднозначности вы можете использовать операции LARGE и SMALL:
JMP SMALL [DWORD PTR ABC]
Данная инструкция приводит к ассемблированию Турбо Ассембле- ром инструкции перехода таким образом, что значение, считываемое из ABC, интерпретируется как 16-разрядный сегмент и 16-разрядное смещение. При этом Турбо Ассемблер выполняет косвенный переход типа FAR (дальний переход).
При использовании операций SMALL или LARGE в адресной части смещения, эти операции указывают, что адрес является 32-разряд- ным, например:
JMP SMALL [LARGE DWORD PTR ABC]
указывает, что переменную в памяти ABC описывает 32-разрядный ад- рес, но ее содержимое интерпретируется как 16-разрядный сегмент и 16-разрядное смещение.
Назад | Содержание | Вперед

Задание адресного подтипа выражения

Турбо Ассемблер обеспечивает операции, которые позволяют вам изменить или переопределить тип выражений. Эти операции перечис- лены в следующей таблице:
Операции переопределения типа Таблица 5.19 ----------------------------T-----------------------------------¬ ¦ Выражение ¦Значение ¦ +---------------------------+-----------------------------------+ ¦выражение_1 PTR выражение_2¦Преобразует выражение_2 к типу, оп-¦ ¦ ¦ределяемому выражением_1. При этом¦ ¦ ¦0=UNKNOWN, 1=BYTE, 2=WORD, 4=DWORD,¦ ¦ ¦6=PWORD, 8=QWORD, 10=TBYTE, 0ffffh=¦ ¦ ¦NEAR, 0fffeh=FAR, все про-¦ ¦ ¦чие=UNKNOWN (только для режима¦ ¦ ¦MASM). ¦ ¦ ¦ ¦ ¦тип PTR выражение_2 ¦Преобразует выражение к заданному¦ ¦ или тип выражение ¦адресному подтипу (только для режи-¦ ¦ ¦ма Ideal). ¦ ¦ ¦ ¦ ¦тип LOW выражение ¦Преобразует выражение к заданному¦ ¦ ¦адресному подтипу. Описанный тип¦ ¦ ¦должен быть меньше по размеру, чем¦ ¦ ¦тип выражения ( только для режима¦ ¦ ¦Ideal). ¦ ¦ ¦ ¦ ¦тип HIGH выражение ¦Преобразует выражение к заданному¦ ¦ ¦адресному подтипу. Описанный тип¦ ¦ ¦должен быть меньше по размеру, чем¦ ¦ ¦тип выражения. Возвращаемый адрес¦ ¦ ¦настраивается на старшую часть объ-¦ ¦ ¦екта, описанного адресным выражени-¦ ¦ ¦ем ( только для режима Ideal). ¦ L---------------------------+------------------------------------
Приведем некоторые примеры:
IDEAL big DD 12345678h MOV ax,[WORD big] ; ax = 5678h MOV al,[BYTE PTR big] ; al = 78h MOV ax,[WORD HIGH big] ; ax = 1234h MOV ax,[WORD LOW big] ; ax = 5678h MOV al,[BYTE LOW WORD HIGH big] ; al = третьему байту big ; = 34h MASM: MOV ax,2 PTR big ; ax = 5678h MOV ax,WORD PTR big ; ax = 5678h (WORD имеет ; значение 2)

Задание элементов структуры и объединения

Турбо Ассемблер включает данные в структуры или объединения построчно. Для выделения памяти для данных и создания элементов при определении структуры и объединения используются те же дирек- тивы, что и для выделения данных и создания меток в открытом сег- менте. Например, директива:
member1 DW 1
одинаково допустима в сегменте и в определении структуры. В сег- менте данный оператор означает "зарезервировать слово со значени- ем 1 и именем member1". В определении структуры или объединения это означает "зарезервировать слово с начальным значением 1 и именем элемента member1".
Если в сегменте или структуре выделяется экземпляр структуры или объединения, вы можете использовать начальное значение эле- мента структуры. Если вы не распределять элементы структуры таким образом, для указания этого используйте в качестве значения дан- ных символ ? (символ неинициализированных данных).
Турбо Ассемблер позволяет использовать в определении струк- туры все методы выделения данных, включая экземпляры других структур, объединений, записей, перечислимых типов данных, таблиц и объектов. Более подробно о выделении данных рассказывается в Главе 12.
В режимах MASM и Ideal имена элементов структур интерпрети- руются по-разному. В режиме MASM имена элементов структур являют- ся глобальными и не могут переопределяться. В режиме Ideal имена элементов структур считаются локальными для структуры или объеди- нения.

Задание модификатора языка

Модификаторы языка указывают сообщают Турбо Ассемблеру, что в процедуры нужно включать специальный код начала и завершения, организующий интерфейс с Windows и менеджером оверлеев VROOM. Чтобы эти модификаторы использовать, укажите их перед языком про- цедуры в директиве модели или в заголовке процедуры. Допустимыми модификаторами являются модификаторы NORMAL, WINDOWS, ODDNEAR и ODDFAR.
Кроме того, вы можете задать используемый по умолчанию моди- фикатор языка в качестве параметра директивы MODEL. Если исполь- зуемый по умолчанию модификатор языка присутствует, то все проце- дуры, в которых не задается модификатор языка, будут использовать соглашения, заданные по умолчанию.
Примечание: Подробнее об этом рассказывается в Главе 7.
Чтобы задать модификатор языка для конкретной процедуры, включите модификатор языка в конкретную процедуру. Например:
. . . sample PROC WINDOWS PASCAL FAR ENDP . . . Если вы не задаете модификатор языка, Турбо Ассемблер ис- пользует модификатор языка, заданный в операторе MODEL. Если ди- ректива MODEL отсутствует, или если задан модификатор NORMAL, то Турбо Ассемблер будет использовать стандартный код начала и за- вершения.
Если вы выбрали модификатор языка WINDOWS, Турбо Ассемблер генерирует код начала и завершения, позволяющий вам выбирать про- цедуру из Windows. Турбо Ассемблер генерирует специальный код на- чала и завершения только для процедура FAR WINDOWS. Из Windows вы не можете вызвать процедуры NEAR, поэтому специальный код начала и завершения для них не требуется. Процедуры, вызываемые Windows, обычно используют соглашения по вызову, принятые в Паскале (PASCAL). Например:
. . . Winoproc PROC WINDOWS PASCAL FAR ARG __hwnd:WORD, __mess:WORD, __wparam:WORD, __lparam:DWORD ; тело процедуры ENDP . . .
Примечание: Подробности о процедурах Windows можно уз- нать в документации по Windows.
Модификаторы языка ODDNEAR и ODDFAR используются для менед- жера оверлеев VROOM. VROOM имеет два режима операций: oddnear и oddfar. Чтобы увидеть код начала и завершения, который порождают эти модификаторы, вы можете использовать параметр командной стро- ки /la.

Задание сегмента

Как мы уже говорили ранее, необходимо обеспечивать, чтобы Borland C++ и Турбо Ассемблер использовали одну и ту же модель памяти, и чтобы сегменты, которые вы используете в Турбо Ассемб- лере, совпадали с теми сегментами, которые использует Borland C++. В Турбо Ассемблере имеется модель памяти tchuge,которая под- держивает модель huge Borland C++. Перечень моделей памяти и сег- ментов можно найти в предыдущем разделе. Нужно не забывать также помещать директиву EXTRN для внешних идентификаторов вне всех сегментов или внутри правильного сегмента.

Закрытие определения структуры или объединения

После того, как вы определите все элементы структуры или объединения, нужно закрыть определение структуры или объединения. Для этого используется директива ENDS.
В режиме Ideal директива ENDS имеет следующий синтаксис:
ENDS [имя]
В режиме MASM вы можете использовать синтаксис:
имя ENDS
где "имя" (если оно присутствует) соответствует имени открытого определения структуры или объединения. Если имя не указано, то закрывается открытая в данный момент структура или объединение.
Вы можете также использовать директиву ENDS для закрытия сегментов. Это не приводит к конфликту, поскольку открытие сег- мента внутри структуры или объединения не допускается.

Замечания по динамически компонуемым библиотекам Windows

Динамически компонуемая библиотека (DLL) представляет собой группу процедур, которую вы можете вызывать из прикладных прог- рамм Windows. Библиотеки DLL расширяют интерфейс прикладных прог- рамм Windows.
Библиотеки DLL выполняют множество функций. Например, вы мо- жете в DLL неинтерактивные программы DOS. С помощью DLL можно добавить новые средства работы с экраном.
На дистрибутивном диске Турбо Ассемблера можно найти пример программы с именем DLLPROG.ASM, который иллюстрирует DLL.
Для построения DLL можно использовать утилиту MAKE. Формиру- ющий файл должен включать в себя все модули, которые должны ком- поноваться с DLL, например:
dllprog.dll: dllprog.obj dllprog.def TLINK dllprog,,,,dllprog RC dllprog.dll
dllprog.obj: dllprog.asm TASM dllprog
Данный процесс построения требует наличия следующего файла определений компоновщика DLLPROG.DEF:
LIBRARY DLLPROG EXETYPE WINDOWS CODE PRELOAD MOVEABLE DISCARDABLE ; CODE применяется к ; сегментам _TEXT или ; в классе CODE DATA PRELOAD MOVEABLE SINGLE ; DATE применяется ко ; всем сегментам в ; группе DGROUP и в ; классе DATA ; (должен быть ; SINGLE для всех DLL HEAPSIZE 0

Замечания по прикладным программам Windows

Прикладная программа Windows во многом аналогична DLL с единственной процедурой с именем WinMain. Windows вызывает WinMain для запуска процедуры. Прикладная программа имеет обычно стандартную структуру, которая позволяет ей взаимодействовать с графической операционной средой Windows.
Пример прикладной программы Windows можно найти в файле WINPROC.ASM на дистрибутивных дисках Турбо Ассемблера. В данном примере для вывода сообщения на экран используются функциональные возможности, обеспечиваемые предыдущим примером DLL.
Для построения прикладной программы Wiondows можно использо- вать утилиту MAKE. При этом в формирующем файле следует указать все модули, компонуемые с данной прикладной программой:
winproc.exe: winprog.obj winprog.def winprogg.res TLINK winprog,,,,winprog RC winprog.res
winproc.res:winproc.rc RC -r winproc.rc
winproc.obj: winprog.asm winprog.inc TASM winprog
Этот процесс построения требует использования следующего файла определений компоновщика WINPROG.DEF:
NAME WINPROG EXETYPE WINDOWS CODE MOVEABLE DISCARDABLE DATA MOVEABLE MULTIPLE DISCARDABLE STACKSIZE 5120 ; минимум для прикладных ; программ Windows
;----------------------------------------------------------- ; Определить импортируемые функции. (Это не обязательно, ес- ; ли вы выполняете компоновку с библиотекой импорта типа ; IMPORT.LIB или LIBW.LIB.) ;----------------------------------------------------------- IMPORTS DLLPROG.SetHello
Назад | Содержание | Вперед

Замечания по программам формата COM

Программы COM представляют собой ограниченные версии прог- рамм EXE. Каждую программу формата COM можно представить как программу EXE, но не каждую программу EXE можно представить как программу формата COM. Здесь действуют следующие ограничения:
- Программы COM следует писать с использованием модели TINY.
- В программах COM нельзя использовать предопределенный сег- мент стека.
- Программа COM не может содержать прямых адресных ссылок на адрес сегмента или группы. Это означает, что программа не может содержать непосредственных дальних вызовов или ссы- латься на сегменты по имени. Все процедуры в программе COM должны описываться как BEAR.
- Выполнение должно начинаться со смещения 100h в сегменте кода. Чтобы это произошло, укажите в качестве первой инс- трукции сегмента кода директиву STURTUPCODE.
Турбо Ассемблер загружает программы COM, начиная со смещения 100h в префиксе программного сегмента программы (PSP). Для этого директива STARTUPCODE для модели TINY автоматически помещает в программу ORG 100h.
При загрузке программы COM устанавливаются следующие регист- ры:
--------------------T-------------------------------------------¬ ¦ Регистр ¦ Значение ¦ +-------------------+-------------------------------------------+ ¦ CS,DS,ES,SS ¦ Содержит адрес параграфа в PSP программы. ¦ ¦ ¦ ¦ ¦ IP ¦ Устанавливается в значение 100h. ¦ ¦ ¦ ¦ ¦ SP ¦ Устанавливается в 0FFFEh (последнее слово ¦ ¦ ¦ в сегменте программы. ¦ L-------------------+--------------------------------------------
Если вы не хотите размещать стек в конце сегмента программы, то нужно установить новый стек. Для такого стека используйте неи- нициализированный сегмент данных (UDATASEG).
Хотя программы COM должны определяться с моделью памяти TINY, с помощью директив CODESEG, DATASEG и UDATASEG можно разде- лить код данные и неинициализированные данные.
Как и в случае программ EXE, когда вы загружаете программу COM, Турбо Ассемблер выделяет для ее завершения всю оставшуюся память. При возврате памяти в DOS убедитесь, что вы не освободи- ли непреднамеренно неинициализированные данные.
Данные вопросы иллюстрируются файлом-примером COMPROPG.ASM, который можно найти на дистрибутивных дисках Турбо Ассемблера.
Чтобы сформировать программу COM, используйте утилиту MAKE. В формирующем файле MAEKFILE следует указать все модули, с кото- рыми компонуется программа, например:
COMPROG.COM: COMPROG.OBJ TLINK COMPROG; COMPROG.OBJ: COMPROG.ASM TASM COMPROG

Замечания по программам формата EXE

При загрузке программы EXE операционная система устанавлива- ет регистры следующим образом:
------------------T---------------------------------------------¬ ¦ Регистр ¦ Значение ¦ +-----------------+---------------------------------------------+ ¦ DS, ES ¦ Содержит адрес параграфа для префикса прог- ¦ ¦ ¦ рамного сегмента программы (PSP). PSP со- ¦ ¦ ¦ держит передаваемые программе в командной ¦ ¦ ¦ строке аргументы и указатель на строку опе- ¦ ¦ ¦ рационной среды для программы. ¦ ¦ ¦ ¦ ¦ CS:IP ¦ Содержит начальный адрес, заданный в опера- ¦ ¦ ¦ торе END в одном из модулей программы, или ¦ ¦ ¦ адрес директивы STARTUPCODE. ¦ ¦ ¦ ¦ ¦ SS:SP ¦ Содержит адрес последнего слова, которое ¦ ¦ ¦ задает в программе сегмент стека. ¦ L-----------------+----------------------------------------------
В программах EXE вы можете задавать любую модель памяти. Следует использовать возможно более простую модель, поскольку это обеспечивает более быстрое выполнение и упрощает программирова- ние. Например, если в вашей программе никогда не предполагается использовать более 64К данных и области стека, то вполне можно использовать модель TINY.
Директива STURTUPCODE в модуле генерирует инструкции, кото- рые автоматически инициализируют все необходимые регистры, соот- ветствующие выбранной модели. Для использования в программе она сохраняет адрес параграфа PSP в сегменте ES.
Когда вы загружаете программу, операционная система выделяет программе до ее завершения всю оставшуюся память. Для программ, которые не используют динамически распределяемую область памяти, или которые строят в памяти свою собственную динамически распре- деляемую область, такое поведение вполне подходит. Другие прог- раммы могут выделять память через DOS. В этом случае через запро- сом на память из DOS память должна быть освобождена и доступна операционной системе.
Эти вопросы иллюстрируются примером программы EXEPROG.ASM на дистрибутивном диске. Чтобы сформировать программу EXE, исполь- зуйте утилиту MAKE. В формирующем файле MAKEFILE следует указать все модули, с которыми компонуется программа, например:
EXECPROG.EXE: EXECPROG.OBJ
TLINK EXECPROG; EXECPROG.OBJ: EXECPROG.ASM TASM EXECPROG

Занесение в стек константа (процессор 8086)

Хотя процессоры 80186б 80286 и 80386 среди основного набора инструкций содержат инструкцию PUSH, которая заносит в стек не- посредственное значение, в процессоре 8086 такой инструкции нет.
Однако Турбо Ассемблер позволяет использовать в инструкции PUSH константы и в процессоре 8086. При этом генерируется после- довательность инструкций, имеющая тот же результат, что и инс- трукция PUSH c операндом-константой для процессора 80186 и выше.
Примечание: Это средство вы можете использовать толь- ко при разрешении генерации эффективного кода.
Последовательность инструкций, которую Турбо Ассемблер ис- пользуется для выполнения операции PUSH с константой, имеет длину около 10 байт. Они короче и выполняются быстрее, чем выполнение той же функции, но их выполнение связано с потерей содержимого регистра, например:
MOV ax, constant PUSH ax
Данная последовательность имеет длину только 6 байт, но в процессе этого теряется содержимое регистра AX.

Записи и массивы

Записи и массивы, занимающие ровно 1, 2 или 4 байта, дубли- руются непосредственно в стек и передаются, как параметры-значе- ния. Если массив или запись имеет какой-либо другой размер (вклю- чая 3 байта), то в стек заносится указатель на этот массив или запись. В этом случае, если подпрограмма модифицирует такую структуру, то она должна создать ее локальную копию.

Запуск программы REVERSE.ASM

Для запуска программы REVERSE.ASM вы должны сначала ассем- блировать ее:
TASM reverse
а затем ввести:
TLINK reverse
для создания выполняемого файла. Запустите программу, введя в от- вет на подсказку DOS слово reverse. В случае вывода при ассембли- ровании сообщений об ошибках, внимательно проверьте исходный код, сравните его с приведенным текстом, а затем попытайтесь повторить процесс.
После запуска программы на экране останется мерцающий кур- сор. Очевидно, программа ожидает, что вы что-нибудь введете. Поп- робуйте ввести:
ABCDEFG
а затем нажмите клавишу ENTER. Программа выведет на экран:
GFEDCBA
и завершит работу.
Теперь ясно, что делает программа REVERSE.ASM: она изменяет порядок символов во введенной строке на обратный. Быстрая работа со строками и символами - эта одна из областей, где язык Ассем- блера превосходно демонстрирует свои качества. Вы увидите это в следующих нескольких главах.
Вас можно поздравить! Вы только что ввели, ассемблировали и скомпоновали несколько программ на Ассемблере и, таким образом, ознакомились в действии с основами программирования на Ассембле- ре: вводом, обработкой данных и выводом.
Если вы не хотите создавать объектный файл, но хотите полу- чить файл листинга, или если вы хотите получить файл перекрестных ссылок, но не хотите создавать файл листинга или объектный файл, задайте в качестве имени файла пустое (нулевое) устройство (NULL). Например, команда:
TASM FILE1,,NUL,
ассемблирует файл FILE1.ASM в объектный файл FILE1.OBJ, не созда- вая файла листинга, и создает файл перекрестных ссылок FILE1.XRF.

Запуск Турбо Ассемблера из DOS

На дистрибутивных дисках находятся две версии Турбо Ассемб- лера - TASM.EXE и TASMX.EXE. Используемая версия зависит от того, что вам нужно сделать.
Примечание: TASMX следует выбрать для ассемблирования очень больших модулей.
Турбо Ассемблер TASMX можно запустить в защищенном режиме Microsoft Windows 3.0 при помощи окна диалога Windows DOS, если вы запустили Windows в улучшенном режиме 386. Используйте TASMX, если у вас имеется большой объем расширенной памяти, и вам требу- ется ассемблировать очень большие модули. TASMX требует задания в маршруте поиска еще одного файла, DPMILOAD.EXE, который также поставляется на дистрибутивных дисках. (При попытке выполнения TASMX в другой среде, нежели MS-Windows в улучшенном режиме 386, TASMX пытается использовать поставляемый с Турбо Ассемблером сер- вер DPMI - поддержку интерфейса защищенного режима DOS. Это поз- воляет вам использовать TASMX на машинах с процессорами 286 или 386 при наличии расширенной памяти.)
Примечание: TASM следует выбирать для того, чтобы иметь большую скорость выполнения ассемблирования.
Если вам не нужно ассемблировать очень большие модули, сле- дует выбрать TASM. Эта версия Турбо Ассемблера работает сравни- тельно быстрее, чем TASMX. Вы также можете запустить TASM, нахо- дясь в диалоговом окне Windows DOS.
Обе версии Турбо Ассемблера имеют очень мощный и гибкий син- таксис командной строки. Если при запуске Турбо Ассемблера не за- дать никаких аргументов, например:
TASM
или
TASMX
то будет выведен экран со вспомогательным описанием параметров командной строки и синтаксиса для задания ассемблируемых файлов. На Рис. 2.1 показан этот экран. Параметры и их синтаксис для TASM такие же.
----------------------------------------------------------------- Turbo Assembler Version 3.0 Copyright (C) 1988,1991 by Borland International, Inc Usage:
TASM [параметры] исх_файл [,объект_файл] [,листинг] [,пер_ссылки]
/a,/s Упорядочивание сегментов по алфавитному порядку или порядку исходного кода
/c Генерация в листинге перекрестных ссылок

/dSYM[=VAL] Определяется SYM = 0 или SYM = VAL

/e,/ r Эмулируемые или действительные инструкции с плаваю- щей точкой

/h,/? Выводится данная справочная информация

/iPATH Включаемые файлы ищутся по маршруту, определяемому PATH

/jCMD Определяет начальную директиву Ассемблера CMD (на- пример, jIDEAL)

/kh# Мощность хеш-таблицы #

/l,/la Генерация листинга: l=обычный листинг, la=расширен- ный

/ml,/mx,/mu Различимость в регистре букв идентификаторов: ml=все, mx=глобальные, mu=не различаются

/mv# Установка максимально допустимой длины имен иденти- фикаторов

/m# Позволяет выполнять # проходов для разрешения ссы- лок вперед

/n Подавление в листингах таблицы идентификаторов

/o,/op Генерация оверлейного кода, 32-разрядные фиксиро- ванные адреса для компоновщика Phar Lap.

/q Подавление записей файла .OBJ, не нужных для ком- поновки

/p Проверка перекрытия сегмента кода в защищенном ре- жиме

/t Подавление сообщений при успешном ассемблировании

/w0,/w1,/w2 Задание уровня предупреждение: w0 - нет предуп- реждений, w1 или w2 - есть предупреждения

/w-xxx,/w+xxx Запрещение или разрешение предупреждения xxx

/x Включение в листинги блоков условного ассемблирова- ния

/z Вывести на дисплей строку исходного текста с сооб- щением об ошибке.

/zi,/zd,/zn Информация об идентификаторах для отладки: zi=пол- ная, zd=только о номерах строк, zn=отсутствует. -----------------------------------------------------------------

Рис. 2.1 Командная строка Турбо Ассемблера

С помощью параметров командной строки вы можете задавать имя одного или нескольких ассемблируемых файлов, а также параметры, управляющие их ассемблированием. В последующих параграфах все ссылки на TASM относятся также к TASMX. Если вы используете TASMX, просто подставляйте в командах вместо TASM TASMX.

Общий вид командной строки выглядит следующим образом:

TASM файлы [; файлы].

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


TASM /e FILE1; /a FILE2

Здесь файл FILE1.ASM ассемблируется с параметром командной строки /e, а файл FILE2.ASM - с параметром командной строки /a.

В общем случае группа файлов в командной строке может иметь вид:

[параметр].исх_файл [[+] исходный_файл]. [,[объектный_файл] [, [файл_листинга], [, [файл_перекрестных_ссылок]]

Этот синтаксис показывает, что группа файлов может начинать- ся с любого параметра, который вы хотите применить к этим файлам, а затем могут следовать файлы, которые вы хотите ассемблировать. Именем файла может быть отдельное имя файла, либо вы можете ис- пользовать обычные трафаретные символы DOS * и ? для задания группы ассемблируемых файлов. Если расширение имени файла не ука- зано, Турбо Ассемблер добавляет расширение .ASM. Например, для ассемблирования всех файлов с расширением .ASM в текущем каталоге введите команду:

TASM *

Если вы хотите ассемблировать несколько файлов, их имена можно разделить знаком плюс (+):

TASM MYFILE1 + MYFILE2

За именем ассемблируемого файла можно задать необязательные имена объектного файла, файла листинга и файла перекрестных ссы- лок. Если вы не задали имен объектного файла или файла перекрест- ных ссылок, то Турбо Ассемблер создаст объектный файл с тем же именем, что и у файла исходного текста и расширением .OBJ.

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

Файл перекрестных ссылок не будет создан, пока вы в явном виде не запросите его создание. Чтобы запросить перекрестные ссылки, поместите после имени файла листинга запятую и за ней имя файла перекрестных ссылок. Если не задать имя файла перекрестных ссылок явно, Турбо Ассемблер создаст этот файл с тем же именем, что и у исходного файла, и расширением .XRF. Если же вы задали имя файла перекрестных ссылок, но без расширения, к имени будет прибавлено расширение .XRF. Описание работы с файлом перекрестных ссылок при помощи утилиты глобальных перекрестных ссылок (TCREF) находится на диске.


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

TASM FILE1,,TEST

Эта строка вызовет ассемблирование файла FILE1.ASM в объект- ный файл FILE1.OBJ и создаст файл листинга TEST.LST.

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

TASM MYFILE,,,MYXREF

По этой команде файл MYFILE.ASM ассемблируется в файл MYFILE.OBJ, листинг выводится в файл с именем MYFILE.LST, а пе- рекрестные ссылки - в файл MYXREF.XRF.

Если при спецификации ассемблируемых исходных файлов вы ис- пользуете трафаретные символы, их можно использовать также для задания имен файла листинга и объектного файла. Например, если в текущем каталоге содержатся файлы XX1.ASM и XX2.ASM, то командная строка:

TASM XX*,YY*

ассемблирует все файлы, начинающиеся с букв XX, генерирует объек- тные файлы, имена которых будут начинаться с YY, а остальную часть имени формирует в соответствии с именем исходного файла. Результирующие объектные файлы получат, таким образом, имена YY1, OBJ и YY2.OBJ.

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

TASM FILE1,,NUL,

Эта команда ассемблирует файл FILE1.ASM в объектный файл FILE1.OBJ. При этом файл листинга не создается, а создается файл перекрестных ссылок FILE1.XRF.

Запуск вашей первой программы

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

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

В некоторых случаях вызываемые из языка С++ функции Ассемб- лера могут использовать (загружать) для обращения к данным ре- гистры DS и/или ES. Полезно знать соотношение между значениями сегментных регистров при вызове из Borland C++, так как иногда Ассемблер использует преимущества эквивалентности двух сегментных регистров. Давайте рассмотрим значения сегментных регистров в тот момент, когда функция Ассемблера вызывается из Borland C++, а также соотношения между сегментными регистрами, и случаи, когда в функции Ассемблера требуется загружать один или более сегментных регистров.
При входе в функцию Ассемблера из Borland C++ регистры CS и DS имеют следующие значения, которые зависят от используемой мо- дели памяти (регистр SS всегда используется для сегмента стека, а ES всегда используется, как начальный сегментный регистр):
Значения регистров при входе в Ассемблер из Borland C++ Таблица 18.2 ------------------------------------------------------------¬ ¦ Модель CS DS ¦ +-----------------------------------------------------------+ ¦ Крохотная _TEXT DGROUP ¦ ¦ Малая _TEXT DGROUP ¦ ¦ Компактная _TEXT DGROUP ¦ ¦ Средняя имя_файла_TEXT DGROUP ¦ ¦ Большая имя_файла_TEXT DGROUP ¦ ¦ Громадная имя_файла_TEXT имя_вызывающего_файла_DATA¦ L------------------------------------------------------------
Здесь "имя_файла" - это имя модуля на Ассемблере, а "имя_вы- зывающего_файла" - это имя модуля Borland C++, вызывающего модуль на Ассемблере.
В крохотной модели памяти _TEXT и DGROUP совпадают, поэтому при входе в функцию содержимое регистра CS равно содержимому DS. При использовании крохотной, малой и компактной модели памяти при входе в функцию содержимое SS равно содержимому регистра DS.
Когда же в функции на Ассемблере, вызываемой из программы на языке С++, необходимо загружать сегментный регистр? Отметим для начала, что вам никогда не придется (более того, этого не следует делать) загружать регистры SS или CS: при дальних вызовах, пере- ходах или возвратах регистр CS автоматически устанавливается в нужное значение, а регистр SS всегда указывает на сегмент стека и в ходе выполнения программы изменять его не следует (если только вы не пишете программу, которая "переключает" стеки. В этом слу- чае вам нужно четко понимать, что вы делаете).

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

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

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

или иначе:

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

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


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

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

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

Значения простых идентификаторов

Турбо Ассемблер возвращает для идентификаторов, которые ис- пользуются сами по себе, следующие значения:
Значения идентификаторов, используемых непосредственно
Таблица 5.10 --------------------------T-------------------------------------¬ ¦ Выражение ¦ Значение ¦ +-------------------------+-------------------------------------+ ¦ имя_адреса ¦ Возвращает адрес. ¦ ¦ ¦ ¦ ¦ имя_числового_выражения ¦ Возвращает значение числового выра- ¦ ¦ ¦ жения. ¦ ¦ ¦ ¦ ¦ имя_таблицы | ¦ Возвращает значение по умолчанию ¦ ¦ имя_элемента_таблицы ¦ элемента таблицы, заданное в опре- ¦ ¦ ¦ делении таблицы. ¦ ¦ ¦ ¦ ¦ структура/ ¦ Возвращает смещение элемента в таб- ¦ ¦ имя_элемента_таблицы ¦ лице или структуре (только в режи- ¦ ¦ ¦ ме MASM). ¦ ¦ ¦ ¦ ¦ имя_записи ¦ Возвращает маску, в которой биты, ¦ ¦ ¦ зарезервированные для представления ¦ ¦ ¦ битовых полей в определении записи, ¦ ¦ ¦ равны 1, а остальные равны 0. ¦ ¦ ¦ ¦ ¦ имя_записи <.> ¦ Возвращает начальное значение эк- ¦ ¦ ¦ земпляра записи, которое имела бы ¦ ¦ ¦ эта запись, если бы она описывалась ¦ ¦ ¦ с текстом, заключенном в угловые ¦ ¦ ¦ скобки (подробности содержатся в ¦ ¦ ¦ Главе 12). ¦ ¦ ¦ ¦ ¦ имя_записи [.] ¦ Аналогично предыдущему. ¦ ¦ ¦ ¦ ¦ имя_поля_записи ¦ Возвращает число бит, на которое ¦ ¦ ¦ смещена запись от младшего бита за- ¦ ¦ ¦ писи (значение сдвига). ¦ ¦ ¦ ¦ ¦ имя_перечисления ¦ Возвращает маску, в которой биты, ¦ ¦ ¦ требуемые для представления макси- ¦ ¦ ¦ мального значения в определении пе- ¦ ¦ ¦ речисления равны 1, а остальные ¦ ¦ ¦ равны 0. ¦ ¦ ¦ ¦ ¦ имя_сегмента ¦ Возвращает значение сегмента. ¦ ¦ ¦ ¦ ¦ имя_группы ¦ Возвращает значение группы. ¦ ¦ ¦ ¦ ¦ имя_структуры/ ¦ Возвращает размер в байтах структу- ¦ ¦ объединения ¦ ры или объединения, но только в том ¦ ¦ ¦ случае, если этот размер равен 1, 2 ¦ ¦ ¦ или 4; в противном случае возвраща- ¦ ¦ ¦ ется 0. ¦ ¦ ¦ ¦ ¦ имя_типа ¦ Если тип определяется как синоним ¦ ¦ ¦ структуры или объединения, то возв- ¦ ¦ ¦ ращаемое значение то же, что и у ¦ ¦ ¦ структуры или объединения. В про- ¦ ¦ ¦ тивном случае возвращается размер ¦ ¦ ¦ типа (с 0ffffh для меток short и ¦ ¦ ¦ near и 0fffeh для меток far). ¦ L-------------------------+--------------------------------------
Все другие типы идентификаторов возвращают значение 0.
Заметим, что когда в выражении вы используете имя текстовой макрокоманды, Турбо Ассемблер подставляет вместо идентификатора текстовой макрокоманды значение текстовой макрокоманды. Аналогич- но, когда вы используете имя псевдонима, Турбо Ассемблер подстав- ляет вместо идентификатора псевдонима значение идентификатора, которое представляет псевдоним.



    Программирование: Языки - Технологии - Разработка