46019 (665326), страница 39
Текст из файла (страница 39)
Для использования в С-программе встроенных ассемблерных кодов может служить опция компилятора -B. Если эта опция не была задана, а в программе встретился встроенный ассемблерный код, то компилятор выдает соответствующее предупреждение и перезапускается с опцией -B.Этого можно избежать, поместив в исходныйкод директиву #pragma inline, которая фактически заставляет компилятор включить опцию -B.
По умолчанию -B запускает TASM. Это умолчание можно переопределить опцией -Exxx, где xxx - это другой ассемблер. Подробную информацию см. в Главе 4, "Компилятор командной строки", Руководства пользователя.
Для использования данного средства вы должны иметь копию Turbo Assembler (TASM). Сначала компилятор генерирует ассемблерный файл, а затем запускает для этого файла TASM, который создает .OBJ-файл.
Разумеется, вы должны быть знакомы с набором команд и архитектурой 8086. Даже если вы не пишете отдельныхмодулей на языкеассемблера, все равно вы должны знать, как именно работают команды ассемблера, как ихприменять ив каких случаях использование этих команд запрещено.
Если все эти условия выполнены, то для включения в С-программу встроенных команд на языке ассемблера достаточно использовать ключевое слово asm. Формат этой команды:
asm код-операции операнды;или новая-строка
где
- код-операции это одна из допустимых команд 8086 (все коды-операций 8086 приводятся ниже в таблице 6.4.
- операнды - это допустимый (допустимые) для данного кода-операции операнд(ы); это могут быть константы, переменные и метки С.
- ;или новая-строка - это либо точка с запятой, либо символ новой строки, обозначающие конец оператора asm.
Новый оператор asm может находиться в той же строке через точку с запятой, однако никакой оператор asm неможет быть продолжен в новой строке.
Если вы хотите включить в программу несколько операторов asm, возьмите их в фигурные скобки:
asm (*
pop ax; pop ds
iret
*)
Точки сзапятой в данном случае не могут служить признаком начала комментария (как в TASM). Длякомментирования операторов asm следует использовать комментарии С, например:
asm mov ax,ds;/* Этот комментарий допустим */
asm (*pop ax; pop ds; iret;*) /* Этот тоже допустим */
asm push ds ;ЭТОТ КОММЕНТАРИЙ НЕВЕРЕН !!
Часть оператораasm, представляющая собой команду на языке ассемблера, непосредственно копируется на выход и встраивается в ассемблерныйкод, генерируемый Turbo C++ из команд С. Символическиеимена С заменяются при этом соответствующими эквивалентами языка ассемблера.
Средствовстроенного ассемблирования не реализует полный ассемблер, в результате чего многие ошибки не обнаруживаются им сразуже. Возможные ошибки обнаруживает TASM. Однако, TASM может оказаться не в состоянии идентифицировать местонахождение ошибки, в частности из-за того, что номер исходной строки С к этому моменту уже утерян.
Каждый оператор asm считается оператором С. Например,
myfunc()
(*
int i;
int x;
if (i>0)
asm mov x,4
else
i = 7;
*)
Данная конструкция представляет собой допустимый оператор С. Отметим, чтоточка с запятой после команды mov x,4 не требуется. Операторы asm являются единственными операторами С, зависящими от наличия символа новой строки. Этоне соответствует практике, принятой для остальной части языкаС, нозато соответствует соглашению, принятому в нескольких компиляторах на базе UNIX.
Ассемблерный оператор может быть использован как в качестве выполняемого оператора внутри функции, так и в качестве внешнего объявления вне этой функции. Ассемблерные операторы, находящиеся вне функций, помещаются в сегмент DATA, анаходящиеся внутри функций помещаются в сегмент CODE.
Ниже приводится версия функции min (которая рассматривалась в разделе "обработка значений возврата" на стр.257 оригинала), использующая встроенное ассемблирование.
int min (int V1, int V2)
(*
asm (*
mov ax,V1
cmp ax,V2
jle minexit
mov ax,V2
*)
minexit:
return (_AX);
*)
Отметим схожесть данного кода с кодом настр.260 оригинала, который используетрасширение Turbo Assembler, связанное с заданием конкретного языка.
В качестве операторов встроенного ассемблирования допускается включать любые кодыопераций 8086. Существует четыре класса команд, позволяемых компилятором Turbo C++:
- обычные команды - стандартный набор кодов операций 8086
- строковые команды - специальные коды обработки строк
- команды перехода - различные коды операций перехода
- директивы ассемблирования - размещения и определения данных
Отметим,что компилятор допускает задания любых операндов, даже если они ошибочны или не разрешены ассемблером. Точный формат операндов не может быть принудительно установлен компилятором.
Коды операций
___________________________________________________________
Ниже приводится полный перечень мнемоническихимен кодов операций, которые могут быть использованы в операторах встроенного ассемблирования:
Мнемонические имена кодов операций Таблица 6.4
aaafdvtr fpatan lsl
aadfeni fprem mov
aamffroe** fplan mul
aasfiadd frndint neg
adcficom frstor nop
addficomp fsave not
andfidiv fscale or
boundfidifr fsqrt out
callfild fst pop
cbwfimul fstcw popa
clcfincstp** fslenv popi
cldfinit fstp push
clifist fstsw pusha
cmcfistp fsub pushf
cmpfisub fsubp rcl
cwdfisubr fsubr rcr
daafld fsubrp ret
dasfld1 ftst rol
decfldcw fweit ror
divfldenv fxam sahf
enterfldl2e fxch sal
f2xm1fldl2t fxtract sar
fabsfldlg2 fyl2x sbb
faddfldln2 fyl2xp1 shl
faddpfldpi hlt shr
foldfldz idiv smsw
fbstpfmul imul stc
fchsfmulp in std
fclexfnclex inc sti
fcomfndisi int sub
fcompfneni into test
fcomppfninit iret verr
fdecstp** fnop lahf verw
fdisifnsave lds wait
fdivfnstcw lea xchg
fdivpfnstenv leave xlat
fdivrfnstsw les xor
При использовании средства встроенного ассемблирования в подпрограммах, эмулирующих операции с плавающей точкой (опцияTCC -O), коды операции, помеченные **, не поддерживаются.
При использовании в операторах встроенного ассемблирования мнемонических команд 80186 необходимо включать опцию командной строки -1. Тогда компилятор включит в генерируемый им ассемблерный код соответствующие операторы, в результате чего Turbo Assembler будет ожидать появление данныхмнемоническихимен.При использовании предыдущих версий ассемблера эти мнемонические имена могут не поддерживаться.
Строковые команды
Помимокодов операций, приведенных выше, возможно использование следующих строковых команд, как в исходном виде, так и с префиксами циклического выполнения.
Строковые команды Таблица 6.5
capslasw movsb capsblods movsw capswlodsb outs laslodsw outsb lasbmovs | outswstos scasstosb scasbstosw scasw |
Префиксы ________________________ | __________________________________ |
Допустимы следующие | префиксы: |
lock rep reperepnerepnzrepz
Команды перехода
Команды перехода рассматриваются отдельно. Поскольку метка не может быть включена в саму команду, переходы выполняются к меткам С (см. раздел "Использование команд перехода и меток" на стр.274 оригинала). В следующей таблице перечисленыдопустимые команды перехода:
Команды перехода Таблица 6.6
jajge jnc jnp jaejl jne jns jbjle jng jnx jbejmp jnge jo jcjna jnl jp jcxzjnae jnle jpe jejnb jno jpo jgjnbe | js jz loop loope loopae loopnz loopz | |
Директивы ассемблирования |
В операторах встроенного ассемблирования Turbo C++ допустимы следующие директивы:
db dd dw extra
Ссылки из операторов встроенного ассемблирования к данным и функциям
В операторах asm допускается использовать символические имена С; Turbo C++ автоматически преобразовывает их в соответствующие операнды языка ассемблера и вставляет перед этими именами символ подчеркивания. Допускается использование любых символических имен, включая автоматически распределяемые (локальные)переменные, регистровые переменные и параметры функций.
В целом, вы можете использовать символическое имя С в любой позиции, где допустимы адресные операнды. Разумеется, допустимо использование регистровых переменных везде, где допустимым операндом является регистр.
Как только ассемблер встречает во время лексического анализа операндов встроенного ассемблера идентификатор, просматривается таблица символических имен С. Имена регистров 8086 из этого поиска исключаются. Имена регистров могут быть набраны какзаглавными, так и строчными буквами.
Встроенное ассемблирование и регистровые переменные
Встроенный ассемблерный код может свободно использовать рабочие регистры SIи DI.При использовании во встроенном ассемблерномкоде регистров SI и DI компилятор не станет распределять их для регистровых переменных.
Встроенное ассемблирование, смещения и переопределение размера
___________________________________________________________
Во времяпрограммирования вам не требуется знать точные смещения локальных переменных. При использовании имени правильное значение смещения будет включено автоматически.
Однако, может оказаться необходимым включение в ассемблерную команду соответствующего WORD PTR, BYTE PTR, или любого другого переопределения размера. Переопределение DWORD PTR требуется задавать в командах LES или косвенного дальнего вызова.
Использование компонентов структур С
___________________________________________________________
В операторе встроенного ассемблирования допускается обращение ккомпонентамструктур обычным способом (т.е. переменная.компонент). В таком случае вы имеете дело с переменной и можете записыватьв нееили обращаться к хранимым в ней значениям. Однако, вы можете также непосредственно обращаться к имени компонента (без имени переменной) в форме числовой константы. В данной ситуации константа равна (в байтах) смещению от начала структуры, содержащей этот компонент. Рассмотрим следующий фрагмент программы:
struct myStruct (*
int a_a;
int a_b;
int a_c;
*) myA;
myfunc ()
(*
...
asm (*mov ax, myA.a_b
mov bx, [di].a_b
*)
...
*)
Мы объявили тип структуры с именем myStruct с тремя компонентами,a_a, a_b и a_c; мы также объявили переменную myA типа myStruct. Первый оператор встроенного ассемблирования пересылает значение из myA.a_b в регистрAX. Второй оператор пересылает значение по адресу [di]+смещение(a_c) в регистр BX(он беретадрес,хранимый в DI, и складывает со смещениемa_c относительно начала myStruct.) В такой последовательностиэти ассемблерные операторы образуют следующий ассемблерный код:
mov ax, DGROUP : myA+2
mov bx, [di+4]
Для чего это может понадобиться? Загрузив регистр (например, DI) адресом структуры типа myStruct вы можетеиспользовать имена компонентов для непосредственных ссылок к этим компонентам. Фактически имя компонента может быть использовано везде, где в качестве операнда ассемблерного операторадопустима числовая константа.
Компоненту структуры обязательно должна предшествовать точка (.), котораясообщает, чтоданноеимя -это имя компонента структуры, а не обычное символическое имя С. Имена компонентов в ассемблерном виде на выходе компилятора заменяются числовыми смещениями (числовое значение a_c равно 4), аинформация о типе теряется. Таким образом, компоненты структуры могут использоваться в ассемблерных операторах как константы времени компиляции.
Однако,здесьсуществует одно ограничение. Еслидве
структуры, используемые во встроенных ассемблерныхоператорах, имеют одинаковые имена, вы должны различать их. Для этого вставьте тип структуры (вкруглых скобках) между точкой и именем компонента, как если бы речь шла о приведении типов. Например,
asm mov bx,[di].(struct tm)tm_hour
Использование команд перехода и меток
___________________________________________________________
Вы можете использовать в операторахвстроенного ассемблирования любые команды условного и безусловного перехода, а такжецикловые команды. Они являются допустимыми исключительно внутри функций. Поскольку операторы asm не позволяют объявления меток, команды перехода ассемблера должны использовать в качестве объектов перехода имена метокgoto C. Прямые дальние переходы генерироваться не могут.
В следующем примере кода переход выполняется к метке C goto a.
int x()
(*