Реферат: Turbo C++ Programer`s guide
Ассемблерный оператор может быть использован как в
качестве выполняемого оператора внутри функции, так и в качестве внешнего
объявления вне этой функции. Ассемблерные операторы, находящиеся вне функций,
помещаются в сегмент 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()
(*
a: /*
это метка команды C goto "a" */
...
asm jmp a
/* переход к метке"a" */
...
*)
Также допустимы косвенные переходы. Для того, чтобы выполнить косвенный
переход, в качестве операнда команды перехода указывается имя регистра.
Функции прерывания
8086 резервирует первые 1024 байта памяти для
набора из 256 дальних указателей, называемых также векторамипрерывания,
указывающих на специальные системные подпрограммы, которые называются
обработчиками прерываний. Эти подпрограммы вызываются при выполнении следующей
команды:
int int#
где int# это число от 0h до FFh. Когда встречается
данная команда, компьютер сохраняет кодовый сегмент (CS), указатель команд (IP)
и состояния флагов, затем запрещает прерывания и выполняет дальний переход по адресу,
на который указывает соответствующий вектор прерывания. Например, часто
встречается прерывание
int 21h
вызывающее большинство подпрограмм DOS. Однако,
многие векторы прерывание не использованы, что означает для вас возможность
написать собственные обработчики прерываний и поместить дальние указатели на
них в свободные векторы прерывания.
Для того, чтобы написать в Turbo C++ обработчик
прерывания, вы должны определить функцию с типом interrupt; более конкретно,
она может выглядеть следующим образом:
void
interrupt myhandler(bp, di, si, ds, es, dx,
cx, bx, ax, ip, cs, flags, ...);
Как можно заметить, все регистры передаютсяв
качестве параметров, что позволяет использовать и модифицировать их в вашей
программе, не прибегая к рассмотренным выше в данной главе псевдопеременным.
Допускается также передачаобработчику прерываний дополнительных параметров
(flags,...); последние должны иметь соответствующие определения.
Функция типа interrupt автоматически сохраняет (помимо SI,
DI и BP) регистры от AX до DX и DS. Эти же регистры при выходе из обработчика
прерывания восстанавливаются.
Обработчики прерываниймогут использовать арифметические
операции с плавающей точкой при всех моделяхпамяти. Любойкод обработчика
прерывания, использующий 80х87, должен сохранять состояние сопроцессора при
входе и восстанавливать его при выходе.
Функция прерывания может модифицировать передаваемые ей
параметры. Изменение объявленных параметров приведет к модификации
соответствующего регистра при выходе из обработчика прерывания. Это свойство
может оказаться полезным, когда обработчик прерывания используется как
служебнаяфункция пользователя, какэто происходит вслучаеслужебной функции DOS
INT
21. Кроме того, обратите внимание на то, что функция прерывания выполняет
выход с помощью команды IRET (возврата из прерывания).
Итак, в каких случаях может понадобиться написать
собственный обработчик прерываний? Делов том, что так работает большинство
резидентных программ. Они инсталлируются какобработчики прерываний. Тем самым,
при выполнении некоторого периодического или специального действия (тактовом
сигнале часов, нажатии клавиши и т.д.) происходит обращение к
соответствующемуобработчику прерывания и соответствующие действия. Затем
управление возвращается программе, при выполнении которой встретилось данное
прерывание.
Практические примеры программ
низкого уровня
Мы уже рассмотрели несколько примеров обращений к
функциям низкого уровня из С-программы; рассмотрим еще несколько таких
практических примеров. Начнем с обработчика прерывания, который выполняет
некоторые безвредные, но ощутимые (в данном случае слышимые) действия: при его
вызове раздается звуковой сигнал.
Прежде всего, напишем саму функцию: Она может выглядеть следующим
образом:
#include <dos.h>
void interrupt mybeep(unsigned bp, unsigned di,
unsigned si, unsigned ds, unsigned es, unsigned dx, unsigned cx, unsigned bx,
unsigned ax)
(*
int i,
j;
char
originalbits, bits;
unsigned char bcount = ax
>> 8;
/* прием
текущих установок управляющего порта */
bits = originalbits =
inportb(0x61);
for (i = 0; i
<= bcount; i++) (
/* временное
выключение динамика */
outportb(0x61,
bits & 0xfc);
for (j = 0; j
<= 100; j++)
; /* пустой оператор */
/* теперь динамик на некоторое
время включается */
outportb(0x61,
bits \! 2);
for (j = 0; j
<= 100; j++)
; /* еще один
пустой оператор */
)
/*
восстановление установок управляющего порта */
outportb(0x61,
originalbits);
*)
Затем напишем функцию, которая будет инсталлировать
данный обработчик прерываний. Ей передается адрес и номер обработчика (от 0 до
255 или от 0x00 до 0xFF).
void
install(void interrupt (*faddr)(), int inum)
(*
setvect(inum,
faddr);
*)
И наконец, вызовем для проверки написанную вами
сигналящую подпрограмму. Это сделает следующая функция:
void
testbeep(unsigned char bcount, int inum)
(*
_AH = bcount;
geninterrupt(inum);
*)
Функция main может иметь вид:
main()
(*
char ch;
install(mybeep,10);
testbeep(3,10);
ch = getch();
*)
Вы можете также сохранить исходный вектор
прерывания и восстановить его при выходе из главной программы. Для этого служат
функции getvect и setvect.
Глава 7Сообщения об ошибках
Turbo C++ различает две категории ошибок: времени
выполнения и времени компиляции. Сообщения об ошибках времени выполнения
выдаются непосредственно при их обнаружении. Сообщения об ошибках времени
компиляции делятся на три категории: фатальные ошибки, не-фатальные ошибки и
предупреждения. Более подробно они описаны, начиная со стр.283 оригинала.
Следующие обобщенные имена и значения используются в перечисленных в
данной главе сообщениях (большинство из них понятны и так и в разъяснениях не
нуждается). В фактических сообщениях об ошибках вместо обобщенных имен и
значений подставляются конкретные.
------------------------------------------------------------
Обобщенное имя
Фактическое имя или значение в данном руководствевыводимое на экран
------------------------------------------------------------
аргументАргумент командной строки или иной аргумент классИмя класса
полеСсылка на
поле
имя_файлаИмя
файла (с расширением или без)
группа Имя
группы
идентификаторИдентификатор
(имя переменной или другой) языкНазвание языка программирования компонентИмя
компонента данных или функции компонента сообщениеСтрока сообщения
модуль Имя
модуля
числоФактическое
число
опцияОпция
командной строки или другая опция параметрИмя параметры
сегментИмя
сегмента
спецификаторСпецификатор
типа символическое_имяСимволическое имя XXXXh4-значное шестнадцатиричное число,
за которым следует h
Сообщения об ошибках перечислены в алфавитном
порядке, по старшинству символов ASCII; обычно первыми идут сообщения,
начинающиеся символами (равенство, запятая, фигурная скобка и т.д.). Поскольку
сообщения, в которых на первом месте выводится имя, специфическое для данной
программы, не могут быть расставлены по алфавиту, то они также находятся в
начальной части каждого списка сообщений.
Например, если у вас имеется функция С++ goforit, то
фактически вы можете получить сообщение об ошибке
goforit must be declared with no
arguments
Для того, чтобы найти описание данного сообщения в этой главе, искать
следует сообщение
Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40
|