Безусловные переход - это такой переход, который передает
управление всякий раз, когда он выполняется. Наоборот, услловный
переход проверяет текущее состояние машины, чтобы определить,
передавать управление или нет. Существует два вида команд
безусловной передачи управления - команды переходов и вызовов.
Все команды вызова CALL - безусловны. Различные команды CALL
показаны на Фиг. 4.27. Близкий вызов CALL, или NEAR CALL, указывает
новое значение регистра IP и сохраняет старое значение регистра IP
в стеке в качестве адреса возврата. Далекий вызов CALL, или FAR
CALL, задает новые значения сегмента и смещения для дальнейшего
выполнения программы и сохраняет в стеке как регистр IP, так и
регистр CS. Близкий непосредственный вызов CALL - это относительный
переход, использующий двухбайтовое поле смещения. Все остальные
команды вызова - абсолютные переходы. Непосредственный вызов FAR
CALL требует четырехбайтовое поле операнда для указания новых
значений для регистров CS и IP. Косвенные переходы используют байт
адресации mod=r/m для указания операнда=регистра или памяти; этот
операнд содержит адрес подпрограммы. Косвенные вызовы типа NEAR
загружают однословный операнд в регистр IP. Вызовы типа FAR
загружают двойное слово из памяти в пару регистров CS:IP; первое
слово загружается в регистр IP, а второе - в регистр CS. Если
команда указывает регистр в качестве операнда косвенного далекого
вызова, результат непредсказуем; микропроцессор 8088 берет новое
значение регистра CS неизвестно откуда. Ни в коем случае нельзя
использовать эту модификацию команды.
Командам CALL соответствуют команды возврата RET. Все возвраты
- косвенные переходы, поскольку они извлекают адрес перехода из
вершины стека. Близкий возврат извлекает из стека одно слово и
помещает его в регистр IP, а далекий возврат извлекает два слова,
помещая слово из меньшего адреса в регистр IP, а слово из большего
адреса в регистр CS.
Программы могут модифицировать возвраты как типа NEAR, так и
типа FAR, указывая параметр счетчика байтов. Команда возврата
прибавляет его значение к указателю стека после извлечения из него
адреса (адресов) возврата. Такая команда позволяет программе
удалять параметры из стека без использования специальных команд
POP; тем самым подчеркивается, что стек - носитель передаваемых
подпрограмме параметров. Такой стиль работы со стеком мы уже
обсуждали во всех подробностях ранее в разделе "Работа со стеком".
Команды безусловного перехода JMP идентичны командам CALL по их
возможностям адресации. Однако существует дополнительная команда
перехода, указывающая однобайтовое смещение для близкого
относительного перехода (команда короткого перехода).
Соответствующей ей команды CALL не существует, так как вызовы
подпрограмм, расположенных поблизости, происходят очень редко.
Команды переходов используют те же методы генерации адреса, что и
команды вызова.
Сделаем сдесь замечание об оптимизации кода и о том, как
работает ассемблер. По мере того, как ассемблер делает первый
переход по тексту программы и назначает адреса командам, он должен
решить, использовать двух= или трехбайтовую разновидность команды
JMP. Если это переход назад, т.е. на место, уже известное
ассемблеру, он может определить правильное смещение; тем самым
ассемблер знает, находится ли переход в диапазоне короткого
смещения. Однако, если переход делается вперед, на метку, о которой
ассемблер еще не знает, он должен предположить, что метка находится
далее, чем 128 байт от текущего места. Затем ассемблер порождает
длинную форму команды перехода. Худший случай ассемблер обязан
выбирать потому, что потом уже не может возвратиться назад и
увеличить размер команды. Затем ассемблер заместит трехбайтовую
команду перехода двухбайтовой командой JMP и однобайтовой командой
NOP, если обнаружит, что переход делается ближе 128 байт от
текущего места. Так как такой переход выполняется несколько
быстрее, время выполнения в этом случае сокращается, но объектный
код остается больше необходимого.
Если программисту заранее известно, что переход вперед делается
на место, лежащее в диапазоне 128 байт от текущего места, он может
об этом сообщить ассемблеру с помощью следующей строки:
JMP SHORT LABEL
Аттрибут SHORT заставляет ассемблер сформировать короткую форму
SHORT команды перехода, даже если он еще не встречал метку. Если же
программист сделал ошибку и переход в действительности не может
быть коротким, ассемблер выдает сообщение об ошибке. На Фиг. 4.26
дан пример оператора SHORT.
Фиг. 4.28 показывает, как можно устроить таблицу переходов
с помощью команды косвенного перехода. В этом примере делается
выбор среди нескольких программ, основываясь на значении аргумента
в регистре AL. Аналогичная программа могла бы вызвать подпрограмму
по индексу. Это - реализация на языке ассемблера оператора CASE,
который существует в некоторых языках высокого уровня.