Переходы
Как уже отмечалось в гл. 1, присущий процессору алгоритм выполнения программы заставляет его выполнять команды программы друг за другом, в том порядке, как они были описаны в исходном тексте программы и содержатся в выполнимом модуле. Однако часто программисту требуется нарушить этот порядок, заставив процессор обойти некоторый участок программы, перейти на выполнение другой ветви или передать управление подпрограмме, имея в виду после ее завершения вернуться на прежнее место. Все эти операции осуществляются с помощью команд переходов.
Переходы разделяются на безусловные, когда передача управления в другую точку программы осуществляется в безусловном порядке, независимо ни от каких обстоятельств, и условные, осуществляемые или не осуществляемые в зависимости от тех или иных условий: результатов сравнения, анализа, поиска и т.п. Безусловные переходы подразделяются на собственно переходы (без возврата в точку перехода) и вызовы подпрограмм (с возвратом после завершения подпрограммы).
Операции переходов и вызовов подпрограмм, помимо их практической ценности, представляют значительный методический интерес, так как затрагивают основу архитектуры процессора – сегментную адресацию памяти. Многочисленные разновидности команд переходов и вызовов обязаны своим существованием не столько потребностям практического программирования, сколько принципиальным архитектурным особенностям процессора. Отчетливое понимание этих особенностей и, соответственно, условий применения и возможностей различных операций переходов необходимо не только при использовании языка ассемблера, но и при программировании на языках высокого уровня, где иногда используется несколько иная терминология, но существо дела остается без изменения.
Безусловные переходы осуществляются с помощью команды jmp, которая может использоваться в 5 разновидностях. Переход может быть:
- прямым коротким (в пределах – 128… + 127 байтов);
- прямым ближним (в пределах текущего сегмента команд):
- прямым дальним (в другой сегмент команд);
- косвенным ближним (в пределах текущего сегмента команд через ячейку с адресом перехода);
- косвенным дальним (в другой сегмент команд через ячейку с адресом перехода).
Рассмотрим последовательно структуру программ с переходами разного вида.
Прямой короткий (short) переход
Прямым называется переход, в команде которого в явной форме указывается метка, на которую нужно перейти. Разумеется, эта метка должна присутствовать в том же программном сегменте, при этом помеченная ею команда может находиться как до, так и после команды jmp. Достоинство команды короткого перехода заключается в том, что она занимает лишь 2 байт памяти: в первом байте записывается код операции (EBh), во втором – смещение к точке перехода. Расстояние до точки перехода отсчитывается от очередной команды, т.е. команды, следующей за командой jmp. Поскольку требуется обеспечить переход как вперед, так и назад, смещение рассматривается, как число со знаком и, следовательно, переход может быть осуществлен максимум на 127 байт вперед или 128 байт назад.
Прямой короткий переход оформляется следующим образом:
code segment … jmp short go; Код ЕВ dd go: … code ends
Если программа транслируется ассемблером TASM, и в строке вызова транслятора указано, что трансляцию следует выполнить в два прохода:
tasm /m2 p,p,p
То описатель short можно опустить, так как ассемблер сам определит, что расстояние до точки перехода укладывается в короткий переход, даже если метка go расположена после строки с командой jmp. При использовании транслятора MASM указание описателя short обязательно (если метка go расположена после команды jmp). Здесь проявляются незначительные различия ассемблеров разных разработчиков.
В комментарии указан код команды: dd (от displacement, смещение) обозначает байт со смещением к точке перехода от команды, следующей за командой jmp.
При выполнении команды прямого короткого перехода процессор прибавляет значение байта dd к младшему байту текущего значения указателя команд IP (который, как уже говорилось, всегда указывает на команду, следующую за выполняемой). В результате в IP оказывается адрес точки перехода, а предложения, находящиеся между командой jmp и точкой перехода, не выполняются. Между прочим, конструкция с прямым переходом вперед часто используется для того, чтобы обойти данные, которые по каким-то причинам желательно разместить в сегменте команд.