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

         

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.



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


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

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

NOW DB ??time

Числовые предопределенные значения можно использовать в лю- бом месте, где допустимо использование числа:

IF ??version GT 100h

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

ASSUME cs:@code

Все предопределенные идентификаторы могут использоваться как в режиме MASM, так и в режиме Ideal.

Если вы при ассемблировании используете параметр командной строки /ml, то предопределенные идентификаторы нужно использовать в точности так, как они описаны ниже (соблюдая регистр символов).

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

@FileName

Заметим, что @FileName представляет собой присваивание псевдонима для текущей ассемблируемой строки.

Исключением являются переопределенные идентификаторы, кото- рые ссылаются на сегменты. Имена сегментов начинаются с символа @ и записываются символами в нижнем регистре. Например:



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


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


@curseg @fardata

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

??date ??version

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


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


Предупреждающие сообщения выдаются для информирования поль- зователей о возможных нежелательных последствиях ассемблирования оператора исходного файла. Предупреждающее сообщение выдается, например, в тех случаях, когда использование 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 = "abc" ABC2 EQU ABC ; ABC2 = "ABC" ABC EQU <det> ; 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