- •Введение
- •1.1. Основные направления эволюции микрокомпьютеров
- •1.2. Основные сведения о компьютерах
- •1.3. Представление чисел
- •Заключение
- •Лекция 2 машинная организация процессора 80286
- •2.1. Введение
- •2.2. Структура памяти
- •2.3. Сегментация памяти
- •2.4. Структура ввода-вывода
- •2.5. Регистры
- •2.6. Операнды и режимы адресации операндов
- •2.7. Замечания о режимах адресации
- •Глава 3 базовая система команд процессора 80286
- •3.1. Нотация языка ассемблера
- •3.2. Команды передач данных
- •3.3. Арифметические команды
- •Лекция 6. Цепочечные команды
- •Лекция 7. Команды безусловной передачи управления
- •Лекция 8. Команды условной передачи управления
- •Лекция 9. Прерывания
- •Int n
- •Лекция 10. Флажковые команды
- •3.10. Команды синхронизации
- •3.11. Поддержка языков высокого уровня
- •3.12. Замечания о префиксах
- •3.13. Воздействие на флажки
- •Заключение
Лекция 6. Цепочечные команды
Под цепочкой понимается последовательность байт или слов в памяти, а цепочечной операцией называется операция, которая выполняется над каждым элементом цепочки. Например, цепочечная передача производит пересылку целой цепочки из одной области памяти в другую. Так как цепочечная операция включает в себя повторения, она может выполняться сравнительно долго. Процессор 80286 имеет набор команд, которые сокращают время выполнения цепочечных операций. Сокращение времени достигается благодаря мощному набору примитивных команд, ускоряющих обработку каждого элемента цепочки, и устранению служебных действий, которые обычно требуются между обработкой последовательных элементов. Примитивные цепочечные команды приведены в табл. 3.18.
|
Таблица 3. 18 Цепочечные примитивы |
| |
|
Мнемоника команды |
Описание команды | |
|
|
ИСТОЧНИК ПРИЁМНИК | |
|
|
Модифицировать SI, DI | |
|
|
ИСТОЧНИК – ПРИЁМНИК ? | |
|
|
Модифицировать SI, DI | |
|
|
AL – ПРИЁМНИК ? | |
|
|
Модифицировать DI | |
|
|
ВХОДНОЙ ПОРТ ПРИЁМНИК | |
|
|
Модифицировать DI | |
|
|
ИСТОЧНИК ВЫХОДНОЙ ПОРТ | |
|
|
Модифицировать SI | |
|
|
ИСТОЧНИК AL | |
|
|
Модифицировать SI | |
|
|
AL ПРИЁМНИК | |
|
|
Модифицировать DI | |
|
В операциях над словами вместо регистра AL используется регистр AX |
| |
Элементарные цепочечные команды. Чтобы показать ускорение обработки цепочек с помощью цепочечных команд, рассмотрим, как передается последовательность байт. Необходимо как-то указать, где находятся байты и где они должны быть. Воспользуемся для этой цели регистрами SI (индекс источника) и DI (индекс приемника). В регистр SI поместим значение смещения первого байта последовательности в текущем сегменте данных, а в регистр DI - значение смещения, по которому этот байт должен быть передан. Число пересылаемых байт удобнее всего загрузить в регистр-счетчик СХ. Если он первоначально содержит нуль, пересылать нечего. Передача цепочки включает в себя следующие действия:
1. Если регистр СХ содержит нуль, операция закончена.
2. Считать байт, смещение которого находится в регистре SI.
3. Запомнить этот байт в ячейке, смещение которой находится в регистре DI.
4. Произвести инкремент содержимого регистра SI на 1.
5. Произвести инкремент содержимого регистра DI на 1.
6. Произвести декремент содержимого регистра СХ на 1.
7. Вернуться к шагу 1 и повторить операцию.
Фактическая передача каждого байта реализуется на этапах 2 и 3, а этапы 1, 4 - 7 являются служебными. Передачу каждого байта можно ускорить, если иметь примитивную команду, которая передает байт, смещение которого находится в регистре SI, в байт, смещение которого содержится в DI. Далее, если эта примитивная команда осуществляет также инкремент содержимого регистров SI и DI, можно устранить часть служебных действий. При наличии такой команды пересылка цепочки упрощается:
1. Если регистр СХ содержит нуль, операция закончена.
2. Выполнить операцию "примитив передачи".
3. Произвести декремент содержимого регистра СХ на 1.
4. Вернуться к шагу 1 и повторить операцию.
Этапы 1, 3 и 4 можно устранить, если примитивная команда будет выполнять действие "проверка, декремент и повторение" над регистром СХ. В результате остается единственный шаг, включающий в себя "примитив передачи". Теперь пересылка цепочки реализуется так:
1. "Усилить" сопровождающий примитив. 1а. Выполнить "примитив передачи".
Команда MOVS (передать элемент цепочки) в процессоре 80286 и является рассмотренным примитивом. Более того, любой цепочечный примитив можно "усилить", указав перед ним специальный однобайтный префикс, называемый префиксом повторения. Комбинация префикса повторения и примитива MOVS образует двухбайтную команду и записывается как REP MOVS.
Если область, в которой размещается исходная цепочка, перекрывается с областью, в которую она пересылается, возникает сложность. Рассмотрим, например, передачу пяти байт с начальным смещением 100 в пять байт, имеющих начальное смещение 102 (рис. 3.15). Байты со смещениями 100 и 101 успешно копируются в байты со смещениями 102 и 103. Но когда подходит очередь копировать байт со смещением 102 в байт со смещением 104, возникает проблема: байт в смещении 102 является не исходным байтом, а байтом, переданным из смещения 100. Следовательно, байт из смещения 100 будет копироваться еще раз, теперь в смещении 104. В конце операции он будет скопирован и в смещении 106. Аналогично байт из 101 будет передан в 103 и 105.
Задача решается, если пересылать байты в обратном порядке, т.е. первым передается байт из смещения 104, затем из 103 и т.д. Но если имеется перекрытие в другом направлении (байты из смещений 100 - 104 передаются в байты смещений 98 - 102), обратный порядок не подходит и нужно передавать в прямом направлении.
Имейте в виду, что "проклятие для одного человека оказывается благом для другого". Проклятие перекрывающихся пересылок цепочек становится очень удобным при необходимости заполнения области памяти одним и тем же набором (тщательно проанализируйте предыдущий пример).
В процессоре 80286 имеется флажок DF направления, который определяет направление обработки цепочек. Если DF = 0, считается, что цепочки размещены в прямом направлении (к большим адресам), начиная со смещений в регистрах SI и DI. Если же DF = 1, цепочки размещены в обратном направлении, поэтому в цепочечных примитивах производится декремент содержимого регистров SI и DI, а не инкремент. Следовательно, в случае перекрывающейся передачи, когда байты пересылаются по большим смещениям и требуется обратная передача, флажок DF необходимо установить в 1. В зависимости от состояния флажка OF в регистрах SI и DI содержатся либо меньшие смещения (DF = 0), либо большие смещения (DF = 1) цепочек. Команды задания состояния флажка DF рассматриваются далее.
Ч
Рис. 3.15 Перекрывающаяся
пересылка
Поэтому для пересылки цепочки из одного сегмента в другой следует загрузить в сегментные регистры DS и ES начальные адреса соответствующих сегментов, а в индексные регистры SI и DI - смещения в этих сегментах. Конечно, пересылка цепочки в пределах одного сегмента реализуется при загрузке в DS и ES одного и того же значения. Передачу цепочки из сегмента, отличающегося от адресуемого DS, можно осуществить, указав префикс замены сегмента (этот префикс воздействует только на операнд-источник).
Некоторые цепочечные операции эффективнее выполнить над словами, а не над байтами. Например, пересылка производится гораздо быстрее, если передаваемыми элементами будут слова. Чтобы обрабатывать цепочки слов, в каждой примитивной команде имеется поле, которое задает операции с байтами или словами. Передача слов отличается от передачи байт тем, что инкремент (декремент, если DF = 1) содержимого регистров SI и DI равен 2, а не 1. Однако декремент содержимого регистра СХ всегда равен 1, поэтому при работе со словами регистра СХ инициализируется на число слов, а не байт.
Вот как выглядят повторяющиеся примитивы пересылок слов и байт:

Отметим, что примитив пересылки производит передачу содержимого ячейки по адресу памяти, смещение которого находится в регистре SI, ячейку по адресу, смещение которого находится в регистре DI. Казалось бы, что в команде не нужно определять никаких операндов, так как у нее нет выбора, какие элементы передавать и куда. Однако ассемблер должен знать, что передавать - байты или слова; он узнает об этом по заданию операндов (они должны быть одного и того же типа). Кроме того, как и в команде XLAT, Ассемблер контролирует нахождение операндов в доступных сегментах.
Рассмотрим теперь другую цепочечную операцию: сканирование последовательности байт в поисках байта, отличающегося от заданного. Примером может служить поиск первого ненулевого элемента в таблице. Вновь предположим, что регистр SI содержит смещение последовательности, а регистр СХ - число байт в ней. Разместим в регистре AL байт, который нужно пропускать. Сканирование состоит из следующих действий:
1. Если регистр СХ содержит нуль, операция закончена.
2. Считать байт, смещение которого находится в регистре SI.
3. Сравнить его с байтом из регистра AL (сравнение предполагает вычитание и установку флажков, в частности, флажка ZF).
4. Произвести инкремент (декремент, если DF = 1) содержимого регистра SI на 1.
5. Произвести декремент содержимого регистра СХ на 1.
6. Если ZF = 1, то два байта одинаковы; вернуться к шагу 1 и повторять.
Шаги 2, 3 и 4 выполняются примитивом сканирования SCAS (сканировать элемент цепочки). Шаги 1, 5 и 6 реализуются, если примитив "усилен" префиксом повторения. Сканирование слов аналогично сканированию байт, но вместо регистра AL используется АХ и инкремент (декремент) производится на 2, а не на 1.
Отметим, что префикс повторения в примитиве сканирования действует иначе, чем в примитиве пересылки; при сканировании перед повторением проверяется состояние флажка ZF. В общем, префикс повторения проверяет флажок ZF, когда примитивная цепочечная операция может его модифицировать. (Команда MOVS никогда не воздействует на флажок ZF, а команда SCAS устанавливает или сбрасывает ZF в зависимости от того, равны байты или нет.)
Еще одна цепочечная операция заключается в сканировании последовательности байт с целью поиска конкретного значения. Если, например, байты содержат символы кода ASCII, эта операция находит первое появление конкретного символа в сообщении. Для этого перед примитивной командой сканирования указывается префикс повторения, как и в предыдущей операции сканирования, но теперь условием повторения становится ZF = 0. Так как проверку состояния флажка ZF определяет префикс повторения, он должен указать, какое состояние флажка ZF вызывает повторение; для этого в префиксе выделен специальный бит.
Ассемблерные мнемоники таких форм повторения имеют вид REPZ (повторять, пока флажок ZF установлен) и REPNZ (повторять, пока флажок ZF сброшен). Альтернативными мнемониками являются REPE (повторять, пока равны) и REPNE (повторять, пока не равны); они более четко показывают условие, при котором осуществляется зацикливание. Когда же префикс повторения не проверяет флажок ZF, он записывается просто как REP (синоним REPZ).
Следующая цепочечная операция сравнивает две последовательности байт для определения того, какая из них оказывается первой. Если, в частности, байты содержат символы в коде ASCII, эта операция проверяет последовательности на лексико-графический порядок (лексико-графический - это просто другое название "алфавитный", но с учетом не только алфавитных символов). Вновь предположим, что смещения двух последовательностей находятся в регистрах SI и DI, а число сравниваемых байт (размер более короткой последовательности) - в регистре СХ. Цепочечное сравнение реализуется такими действиями:
1. Если регистр СХ содержит нуль, операция закончена.
2. Считать байт, смещение которого находится в регистре SI.
3. Сравнить его с байтом, имеющим смещение в регистре DI.
4. Произвести инкремент (декремент, если DF = 1) содержимого регистра SI на 1.
5. Произвести инкремент (декремент, если DF = 1) содержимого регистра DI на 1.
6. Произвести декремент содержимого регистра СХ на 1.
7. Если ZF = 1, то два байта одинаковы, поэтому вернуться к шагу 1 и повторить операцию.
Шаги 2, 3, 4 и 5 реализуются примитивом сравнения CMPS (сравнивать элементы цепочек), а остальные шаги выполняются, если к команде CMPS добавлен префикс повторения REPE. Сравнение слов аналогично сравнению байт, но инкремент или декремент содержимого регистров SI и DI производится на 2, а не на 1.
Рассмотрим операцию сравнения подробнее. Когда сравниваемые на шаге 3 байты одинаковы, флажок ZF устанавливается в 1 и шаг 7 вызывает зацикливание. Оно прекращается, когда либо байты различаются (и шаг 7 не вызывает повторения), либо достигается конец более короткой цепочки (на шаге 1 осуществляется выход из цикла). После окончания цикла по флажку ZF можно проверить, не достигнут ли конец более короткой цепочки (в этом случае ZF = 1). Можно также проверить флажок переноса CF, чтобы узнать, какая цепочка длиннее (CF = 1 означает, что длиннее цепочка, адресуемая регистром DI).
Следующие два цепочечных примитива обеспечивают считывание данных из входного устройства в последовательные ячейки памяти и запись данных из последовательных ячеек памяти в выходное устройство. Они упрощают передачи больших блоков данных между памятью и внешними устройствами; блоковые передачи часто требуются для дисковых накопителей. Примитив ввода INS передает данные из входного порта, определяемого содержимым регистра DX, в байт или слово, смещение которого находится в DI, и производит инкремент (декремент, если DF = 1) содержимого регистра DI на 1 или 2. Аналогично примитив вывода OUTS передает байт или слово, смещение которого находится в регистре SI, в выходной порт, адресуемый регистром DX, и производит инкремент (декремент, если DF = 1) содержимого регистра SI на 1 или 2. Команд INS и OUTS в микропроцессоре 8086 нет.
Последние два цепочечных примитива - это LODS (загрузить элемент цепочки) и STOS (запомнить в элементе цепочки). Примитив загрузки передает байт или слово, смещение которого находится в регистре SI, в аккумулятор AL или АХ и производит инкремент (декремент, если DF = 1) содержимого регистра SI на 1 или 2. Примитив запоминания передает байт или слово, содержащееся в регистре AL или АХ, в байт или слово, смещение которого находится в регистре DI, и производит инкремент (декремент, если DF = 1) содержимого регистров DI на 1 или 2. В отличие от предыдущих примитивов LODS и STOS не предназначены для использования с префиксом повторения, а введены для построения более сложных цепочечных операций. Однако примитив запоминания реализует полезную функцию и с префиксом
повторения: он заполняет последовательные байты или слова одним и тем же значением. (Эту функцию можно реализовать и с помощью пересылки цепочки, но здесь потребуются две цепочки вместо одной.) Префикс повторения в примитиве загрузки абсолютно бесполезен: он повторно загружает в регистр AL или АХ последовательные байты или слова цепочки, всякий раз разрушая предыдущее загруженное значение.
Сложные цепочечные команды. Пять примитивных цепочечных команд осуществляют наиболее часто встречающиеся цепочечные операции. Наверное, было бы бессмысленно разрабатывать примитивные команды для всех возможных операций. Удобнее предусмотреть средства построения эффективных сложных цепочечных команд, возможно, с привлечением некоторых примитивов. Рассмотрим, например, операцию изменения знака последовательности байт, в которой каждый байт представляет собой 8-битное знаковое число. Пусть регистр SI содержит смещение первого байта последовательности, a DI - смещение первого байта той области, в которую помещается последовательность с измененным знаком. Предположим также, что регистр СХ содержит число байт в последовательности. Операция состоит из следующих шагов:
1. Если регистр СХ содержит нуль, операция закончена и остальные шаги пропускаются.
2. Считать байт, смещение которого находится в регистре SI.
3. Произвести инкремент содержимого регистра SI на 1.
4. Изменить знак считанного байта.
5. Запомнить результат в байте, смещение которого содержится в регистре DI.
6. Произвести инкремент содержимого регистра DI на 1.
7. Произвести декремент содержимого регистра СХ на 1.
8. Вернуться к шагу 1, если значение содержимого регистра СХ не равно 0.
Как и в предыдущих примерах, удобно иметь примитивную команду, которая реализует шаги 2 - 6. Но такой команды нет! Поэтому придется выполнять указанные шаги с помощью имеющихся команд процессора 80286. Если привлечь цепочечные примитивы, то инкремент содержимого регистров SIиDIвыполняется без дополнительных команд. Как видно, шаги 2 и 3 можно реализовать примитивом загрузки, шаг 4 - командой изменения знака, а шаги 5 и 6 - примитивом запоминания. Получаются следующие действия:
1. Если регистр СХ содержит нуль, операция закончена и остальные шаги пропускаются.
2. Выполнить примитив загрузки.
3. Изменить знак байта в регистре AL.
4. Выполнить примитив запоминания.
5. Произвести декремент содержимого регистра СХ на 1.
6. Вернуться к шагу 1, если значение содержимого регистра СХ не равно 0.Ранее шаги 1, 5 и 6 осуществлялись посредством "усиления" цепочечного примитива префиксом повторения. Здесь же тело цикла состоит не только из цепочечного примитива, поэтому использовать префикс повторения нельзя. Потребуются менее эффективные команды, которые моделируют сложные действия префикса повторения. Шаг 1 потребует команды условного перехода, вызывающей передачу управления, если регистр СХ содержит нуль. Назначение перехода необходимо определить как можно короче. Поэтому неудивительно, что в процессоре 80286 есть команда JCXZ, которая передает управление, если регистр СХ содержит нуль. Назначение перехода задано в команде одним байтом; в нем находится разность (как знаковое число) между значением смещения назначения и значением смещения команды JCXZ. Желательно также иметь команду, которая производит декремент содержимого регистра СХ и осуществляет переход, если в СХ не содержится нуль. Такая команда тоже имеется и называется LOOP; назначение перехода в команде LOOP задано одним байтом, как и в команде JCXZ. Теперь наш пример превращается в следующие действия:
1. Выполнить команду JCXZ.
2. Выполнить операцию "примитив загрузки".
3. Изменить знак байта в регистре AL.
4. Выполнить операцию "примитив запоминания".
5. Выполнить команду LOOP.
З
десь
каждый шаг реализуется всего одной
командой:
Команда LOOP осуществляет переход в зависимости от содержимого регистра СХ. Но, как мы видели ранее, иногда желательно повторение цикла с учетом состояния флажка ZF. Соответствующими командами процессора 80286 являются LOOPZ (зациклить, если ZF = 1) и LOOPNZ (зациклить, если ZF = 0). Конечно, обе команды LOOPZ и LOOPNZ перед зацикливанием производят декремент содержимого регистра СХ и проверяют его на нуль. Команды имеют альтернативные мнемоники LOOPE (зациклить, если равны) и LOOPNE (зациклить, если не равны); они более наглядно показывают условие зацикливания.
Для примера использования команды LOOPNZ обратимся к изменению знака последовательности байт, но теперь число байт не определено. Известно, однако, что ни один байт в последовательности не содержит нуля, но вся последовательность заканчивается нулевым байтом. Напомним, что команда NEG устанавливает флажок ZF, если байт-операнд содержит нуль. Получаются следующие действия:

Отметим, что первая команда JCXZ оказалась ненужной. Подумайте, почему?
В заключение рассмотрим пример преобразования чисел от 0 до 15 в код Грея. В этом коде соседние значения отличаются только одним битом; он имеет такой вид:
-
Двоичное число
Код Грея
0000
0000
0001
0001
0010
0011
0011
0010
0100
0110
0101
0100
0110
0101
0111
0111
1000
1111
1001
1110
1010
1100
1011
1101
1100
1001
1101
1011
1110
1010
1111
1000
Предположим, что в текущем сегменте данных со смещением МЕМВ1 . находится последовательность байт, содержащих двоичные числа от 0 до 15. Регистр СХ содержит некоторое число байт в последовательности.
Далее,
пусть последовательность из 16 байт в
текущем сегменте данных со смещением
GRAY
представляет собой таблицу преобразования
в код Грея. О
тметим,
что заданные условия идеальны для
применения командыXLAT.
Разместим преобразованную последовательность
в дополнительном сегменте данных со
смещением МЕМВ2. Получается следующий
фрагмент преобразования:
Команда XLAT специально разработана с учетом цепочечных циклов.

MOVS
(передать)
CMPS
(сравнить)
SCAS
(сканировать)
INS
(ввести)
OUTS
(вывести)
LODS
(загрузить)
STOS
(запомнить)