- •Введение
- •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. Воздействие на флажки
- •Заключение
Лекция 7. Команды безусловной передачи управления
К командам настоящей группы относятся команды переходов, вызовов (подпрограмм) и возвратов (из подпрограмм). Переходы загружают значение в указатель команды IP, нарушая этим последовательное выполнение команд. Вызовы выполняют то же самое, но вначале они запоминают текущее значение содержимого указателя IP в стеке, так что в последующем можно возобновить выполнение программы с этой точки. Возвраты и осуществляют это возобновление; они берут элемент из стека и возвращают его в указатель команды. Вызовы и возвраты - средства организации процедур. Но пока ничего нового в сказанном нами нет.
Новым в процессоре 80286 является то, что переходы, вызовы и возвраты бывают двух видов - внутрисегментные и межсегментные. Первые из них передают управление внутри текущего сегмента кода, а вторые - в произвольный сегмент кода (изменяя содержимое регистра CS), который становится текущим сегментом кода.
Очевидно, межсегментные передачи управления могут сделать все, что делают внутрисегментные, и что-то еще. Зачем же потребовались две разновидности команд? Просто потому, что межсегментные передачи управления выполняются дольше (ведь они и делают больше); кроме того, за исключением возвратов, они оказываются длиннее (им нужно больше информации).
В
примере межсегментного перехода
предположим, что текущий сегмент кода
начинается в В 0000 и что указатель команды
содержит 00А0. Это значит, что следующей
выполняется команда по адресу В00А0.
Пусть
по этому адресу находится команда
перехода, которая передает управление
ячейке А0100. Текущий сегмент кода занимает
диапазон от В0000 доBFFFF,
поэтому переход к ячейке А0100 должен
быть межсегментным. Он должен
определять новое значение для регистра
CS
(возможно, А000) и новое значение для
IP
(0100). Пример этой команды показан на рис.
3.16.
В программе межсегментный и внутрисегментный переходы обозначаются мнемоникой JMP; ассемблер может узнать, находится ли назначение в том же сегменте, что и команда перехода, и образовать соответствующую команду.
Межсегментный вызов сохраняет в стеке текущее содержимое регистров CS и IP. Межсегментный возврат берет из стека два 16-битных значения и помещает их в регистры IP и CS. В случае внутрисегментного вызова и возврата в стеке сохраняется и восстанавливается только указатель команды IP. Отметим, что все вызовы процедуры должны быть либо межсегментными, либо внутрисегментными, так как возвраты в процедуре должны извлекать из стека такое же число байт, какое включает в стек каждый вызов. Поэтому может оказаться, что для вызова процедуры в том же самом сегменте применяется межсегментный вызов просто потому, что в некотором другом сегменте есть еще один вызов (обязательно межсегментный) этой же процедуры.
Поскольку UPCOUNT объявлена процедурой NEAR (близкой), все вызовы ее ассемблируются как внутрисегментные, а все возвраты в ней - так же, как внутрисегментные. Запись FAR (далекая) вместо NEAR заставит все вызовы и возвраты быть межсегментными.
Предыдущий пример межсегментного перехода показывает еще и принцип прямой передачи управления. Прямой переход (или вызов) немедленно указывает, куда направляться. Косвенный же переход несколько сложнее; он сообщает, куда обратиться, чтобы узнать, куда направляться. Косвенные передачи управления полезны, когда неизвестно дальнейшее направление и необходимо сначала вычислить назначение. Например, косвенный межсегментный переход или вызов определяет первый из четырех смежных байт в памяти, которые содержат новое значение в регистре IP (два байта) и новое значение в регистре CS (два байта). Эти значения могут быть вычислены в предыдущих командах.
Возвраты никогда не сообщают, куда направляться; вместо этого они заставляют вернуться туда, откуда произведен вызов. Поэтому понятие косвенного возврата смысла не имеет.
В табл. 3.19 показаны примеры безусловных переходов, вызовов и возвратов. Как мы говорили, ассемблерные операторы прямых внутрисегментных переходов и вызовов выглядят так же, как и межсегментные, и никаких трудностей для ассемблера здесь не возникает. Рассмотрим, однако, косвенную передачу управления, например JMP MEMLOC.
|
|
|
Таблица 3. 19 Примеры команд безусловной передачи управления |
|
| |||||
|
|
Внутрисегментные |
Межсегментные |
|
| |||||
|
Прямые |
Косвенные |
Прямые |
Косвенные |
| |||||
|
Переход |
JMP, LABEL |
JMP, MEMLOC |
JMP, LABEL |
JMP, DWORD PTR, MEMLOC |
| ||||
|
Вызов |
CALL, LABEL |
CALL, MEMLOC |
CALL, LABEL |
CALL, DWORD PTR, MEMLOC |
| ||||
|
Возврат |
RET |
|
RET |
|
| ||||
|
Возврат с извлечением |
RET 6 |
|
RET 6 |
|
| ||||
|
Здесь LABEL – символическая метка, предшествующая некоторой команде, например LABEL: MOV AX, BX |
|
| |||||||
Чтобы определить, является этот переход межсегментным или внутрисегментным, ассемблер должен знать, будет ли MEMLOC первым из двух смежных байт (словом - WORD) или четырех смежных байт (двойным словом, или DWORD). Это можно определить в операторе, который распределяет пространство для метки MEMLOC, или явно указать в операторе передачи управления некоторым ключевым словом перед MEMLOC. Например, команда
![]()
означает переход с использованием двухсловного указателя с именем MEMLOC и, следовательно, должна быть межсегментным переходом.
Внутрисегментный переход определяет новое значение только для регистра IP. Рассмотрим, например, команду перехода со смещением 01А8 в текущем сегменте кода, которая должна заставить программу вернуться на 8 байт к смещению 01А0. Значение смещения 01А0 может находиться в двух байтах команды перехода; так и сделано во многих процессорах. Однако у такого способа есть два недостатка. Во-первых, диапазон многих переходов составляет примерно 100 байт, а в команде для назначения приходится все равно выделять два байта. Во-вторых, если по какой-то причине вся секция кода от смещения 01А0 до 01В0 должна быть размещена от смещения 0500 до 0510, команда перехода, определяющая смещение 01А0, больше не будет переходить на 8 байт назад. (Секции кода, которые можно перемещать с последующим правильным выполнением, иногда называются позиционно-независимым кодом.) Если команда перехода будет определять просто -8, а не 01А0, то назначение сокращается на один байт и код становится позиционно-независимым. Поэтому в прямых внутрисегментных переходах и вызовах указывается не смещение назначения, а разность (как знаковое число) между смещением назначения и смещением самой команды перехода или вызова. Более того, если эта разность в команде перехода может поместиться в 8 битах (что бывает довольно часто), можно использовать короткую форму команды прямого внутрисегментного перехода, которая на один байт короче обычной команды. Короткая форма команды вызова отсутствует, так как близкие вызовы встречаются не часто.
Разумеется, мы не должны сами вычислять эти разности. Нам нужно только записать в команде JMP или CALL символическое имя назначения, а ассемблер выполнит вычитание и образует правильные байты команды.
Мы показали две причины применения относительных, а не фактических смещений, как назначений переходов. Покажем, что нет веских причин не пользоваться относительными смещениями. Фактическое смещение представляет собой 16-битное беззнаковое число (из диапазона от 0 до 65535) и может обозначать любую ячейку в текущем сегменте кода.
Может ли относительное смещение, будучи знаковым числом (-32768 -+ 32767), перекрыть такой же диапазон? Существует ли, например, такое относительное смещение, которое можно использовать в команде перехода со смещением 0 для достижения смещения 65535? Наибольшее положительное относительное смещение равно +32767, а это всего половина расстояния. Поэтому перейдем к отрицательным относительным смещениям. Поскольку команда перехода уже имеет минимальное смещение в сегменте, куда же приведет отрицательное относительное смещение, равное -1? Ответ таков: к наибольшему смещению в сегменте, т.е. 65535 (так спроектирован процессор). Фактически команда перехода со смещением 0 может достичь любого смещения от 32768 до 65535 при использовании отрицательного относительного смещения. Теперь должно быть ясно, что относительные смещения могут привести от команды перехода, расположенной в любом месте сегмента, к любой ячейке в сегменте.
Наше рассуждение об использовании относительных смещений вместо фактических неприменимо ни к косвенным переходам и вызовам, ни к межсегментным. Объясняется это несколькими причинами:
1. Косвенные переходы и вызовы не определяют назначение, а показывают, где его найти. Несколько команд косвенных переходов и вызовов могут определять одно и то же место, в котором находится назначение. Поэтому принцип относительного смещения теряет смысл и неизвестно, относительно какой команды его находить.
2. Межсегментные переходы и вызовы задают назначения в другом сегменте кода. Если секция кода, содержащая межсегментные переходы или вызовы, перемещается в памяти, назначение, будучи в другом сегменте, совсем не обязательно будет изменяться. Следовательно, применение относительных смещений не приводит к позиционно-независимому коду.
3. Назначение межсегментного перехода или вызова не обязательно находится рядом, поэтому нельзя ожидать экономии байт при использовании относительных смещений.
В заключение несколько слов о команде возврата. Имеется разновидность команды возврата, которая после восстановления значений содержимого регистра IP и, возможно, регистра CS (их значения извлекаются из стека) прибавляет к значению указателя стека константу, содержащуюся в команде как непосредственный операнд. В результате из стека извлекаются и уничтожаются дополнительные элементы. Значения этих элементов можно поместить в стек до команды вызова, т.е. передать вызываемой процедуре параметры. Когда процедура завершается и осуществляет возврат, параметры больше не нужны. Приведенная выше команда возврата оказывается удобным средством удаления параметров процедуры. Если бы такой команды не было, параметры пришлось бы удалять из стека следующим образом:
1. До возврата процедура считывает из стека сохраненное значение в регистре IP (и, возможно, в регистре CS) и помещает его куда-то в память. Этим действием "раскрываются" параметры, находящиеся в стеке под сохраненными значениями в регистрах IP и CS.
2. Затем процедура прибавляет к содержимому регистра SP константу, что эквивалентно удалению параметров.
3. После этого процедура возвращает в стек "отложенные" значения в регистрах IP и CS.
4. Выполняется команда возврата.
Как видно, команда "возврата и удаления" значительно упрощает удаление параметров из стека.
Можно также удалить параметры путем инкремента содержимого указателя стека после того, как процедура выполнит команду возврата. На первый взгляд это представляется почти столь же эффективным, как и команда "возврата и удаления". Но производить инкремент содержимого указателя стека сама процедура не может (уже произведен возврат из нее), поэтому его нужно выполнять в каждом том месте, куда возвращается процедура. Как только вы убедитесь, что процедура может вызываться из многих мест, такое решение будет менее привлекательным.
В команде "возврата и удаления" число параметров (т.е. значение, которое прибавляется к содержимому регистра SP) представлено 16 битами. Конечно, в подавляющем большинстве случаев было бы достаточно 8 бит и команда оказалась бы на один байт короче. Однако в тех редких случаях, когда 8 бит недостаточно, пришлось бы удалять параметры тем менее удобным способом, который рассмотрен выше. Поэтому в команду был введен лишний байт.
