Лекция 6 . Команды микропроцессора. Группа команд передачи управления. Циклы.
Команды передачи управления изменяют содержимое регистров cs и eip/ip в результате чего микропроцессор выбирает для выполнения не следующую по порядку команду программы, а команду в некотором другом участке программы. Конвейер внутри микропроцессора при этом сбрасывается.
По принципу действия команды микропроцессора, обеспечивающие организацию переходов в программе, можно разделить на три группы:
1. Команды безусловной передачи управления:
- команда безусловного перехода;
- вызов процедуры и возврата из процедуры;
- вызов программных прерываний и возврат из программных прерываний.
2. Команды условной передачи управления:
- команды перехода по результату команды сравнения;
- команды перехода по состоянию определенного флага;
- команды перехода по содержимому регистра есх/сх.
3. Команды управления циклом:
- команда организации цикла со счетчиком есх/сх;
- команда организации цикла со счетчиком есх/сх с возможностью досрочного выхода из ци- near — переход на эту метку возможен только в пределах сегмента кода, где эта метка описана. Физически это означает, что для перехода на метку достаточно изменить только содержимое регистра eip/ip;
- far — переход на эту метку возможен только в результате межсегментной передачи управления, для осуществления которой требуется изменение как содержимого регистра eip/ip, так и регистра cs.
Каждая команда во время трансляции имеет адрес, равный значению счетчика адреса команд. счетчик адреса растет только после тех строк исходной программы, которые генерируют некоторое машинное представление (в том числе после директив резервирования и инициализации данных в сегменте данных).
Транслятор ассемблера предоставляет две возможности работы с этим счетчиком:
- Использование меток, атрибуту смещения которых транслятор присваивает значение счетчика адреса той команды, перед которой они появились.
- Применение специального символа $ для обозначения счетчика адреса команд. Этот символ позволяет в любом месте программы использовать численное значение счетчика адреса. Классический пример:
.data
; вычисление длины строки в сегменте данных
Str_Mes db ".Работаешь на ПК - изучи ассемблер”,’$’
Len_Msg=$-Str_Mes
После ассемблирования значение Len_Msg будет равно длине строки, так как значение символа $ в месте его появления отличается от Str_Mes ровно на длину строки.
Кроме возможности получения значения счетчика адреса, компилятор TASM позволяет, при необходимости, установить счетчик адреса в нужное абсолютное значение. Это делается с помощью директивы ORG:
ORG выражение - задает значение счетчика адреса. Выражение должно быть таким, чтобы ассемблер мог преобразовать его к абсолютному числу при первом проходе трансляции.
Безусловные переходы осуществляются с помощью команды jmp, которая может использоваться в 5 разновидностях. Переход может быть:
- прямым коротким (в пределах -128...+127 байтов); прямым ближним (в пределах текущего сегмента команд); прямым дальним (в другой сегмент команд);
- косвенным ближним (в пределах текущего сегмента команд через ячейку с адресом перехода);
косвенным дальним (в другой сегмент команд через ячейку с адресом перехода).
Рассмотрим последовательно структуру программ с переходами разного
вида.
Прямой короткий (short) переход. Прямым называется переход, в команде которого в явной форме указывается метка, на которую нужно перейти. Метка должна присутствовать в том же программном сегменте, при этом помеченная ею команда может находиться как до, так и после команды jmp. Команда короткого перехода занимает 2 байт памяти: в первом байте записывается код операции (EBh), во втором — смещение к точке перехода. Расстояние до точки перехода отсчитывается от очередной команды, т.е. команды, следующей за командой jmp. Поскольку требуется обеспечить переход как вперед, так и назад, смещение рассматривается, как число со знаком и, следовательно, переход может быть осуществлен максимум на 127 байт вперед или 128 байт назад. Прямой короткий переход оформляется следующим образом:
code segment
jmp short go ;Код ЕВ dd
go: code ends
Прямой ближний (near), или внутрисегментный переход. Этот переход отличается от предыдущего только тем, что под смещение к точке перехода отводится целое слово. Это дает возможность осуществить переход в любую точку 64-кбайтного сегмента.
code segment
jmp go ;Код EB dddd
go: code ends
Прямой дальний (far), или межсегментный переход позволяет передать управление в любую точку любого сегмента. При этом, очевидно,
предполагается, что программный комплекс включает несколько сегментов
команд. Команда дальнего перехода имеет длину 5 байт и включает, кроме кода
операции EAh, еще и полный адрес точки перехода, т.е. сегментный адрес и смещение. Транслятору надо сообщить, что этот переход — дальний (по умолчанию команда jmp транслируется, как команда ближнего перехода). Это делается с помощью описателя far ptr, указываемого перед именем точки перехода.
codel segment
assume CS:codel ;Сообщим транслятору, что это сегмент команд jmp far ptr go ;Код EA dddd ssss • • •
codel ends code2 segment
assume CS:code2 ; Сообщим транслятору, что это сегмент команд go:
code2 ends
Метка go находится в другом сегменте команд этой двухсегментной программы. В коде команды ssss — сегментный адрес сегмента code2, a dddd — смещение точки перехода go в сегменте команд code2.
Косвенный ближний (внутрисегментный) переход. В отличие от команд прямых переходов, команды косвенных переходов могут использовать различные способы адресации и, соответственно, иметь много разных вариантов. Общим для них является то, что адрес перехода не указывается явным образом в виде метки, а содержится либо в ячейке памяти, либо в одном из регистров. Это позволяет при необходимости модифицировать адрес перехода, а также осуществлять переход по известному абсолютному адресу. Рассмотрим случай, когда адрес перехода хранится в ячейке сегмента данных. Если переход ближний, то ячейка с адресом состоит из одного слова и содержит только смещение к точке перехода. code segment jmp DS:go_addr ; go: ;Точка перехода
code ends data segment
go addr dw go ;Адрес перехода (слово)
data ends
Точка перехода go может находиться в любом месте сегмента команд. В коде команды dddd обозначает относительный адрес слова go_addr в сегменте данных, содержащем эту ячейку.
В приведенном фрагменте адрес точки перехода в слове go_addr задан однозначно указанием имени метки go. Такой вариант косвенного перехода выполняет фактически те же функции, что и прямой (переход по единственному заданному заранее адресу), только несколько более запутанным образом. Достоинства косвенного перехода будут более наглядны, если представить, что ячейка go_addr поначалу пуста, а по ходу выполнения программы в нее, в зависимости от каких-либо условий, помещается адрес той или иной точки перехода:
mov go_addr, offset go1 mov go_addr, offset go2 mov go_addr, offset go3
Косвенный дальний (межсегментный) переход. Как и в случае ближнего перехода, переход осуществляется по адресу, который содержится в ячейке памяти, однако эта ячейка имеет размер 2 слова, и в ней содержится полный (сегмент плюс смещение) адрес точки перехода. Программа в этом случае должна включать по меньшей мере два сегмента команд. Структура программы с использованием косвенного дальнего перехода может выглядеть следующим образом:
codel segment assume CS:codel, DS:data
jmp DS:go addr; Код FF 2E dddd
codel ends code2 segment assume CS:code2
go: ;Точка перехода в другом сегменте команд
code2 ends data segment
go addr dd go ;Двухсловный адрес точки перехода
data ends
Точка перехода go находится в другом сегменте команд этой двухсегментной программы. В коде команды dddd обозначает относительный адрес слова go_addr в сегменте данных. Ячейка go_addr объявляется директивой dd (define double, определить двойное слово) и содержит двухсловный адрес точки перехода; в первом слове содержится смещение go в сегменте команд codel, во втором слове сегментный адрес code2. Оба компонента адреса перехода могут быть вычислены и помещены в ячейку go_addr по ходу выполнения программы.
Как и в случае ближнего косвенного перехода, ассемблер допускает различные формы описания дальнего косвенного перехода через ячейку сегмента данных:
jmp DS:go_addr ;Возможна замена - сегмент
jmp dword ptr go_addr ;Если поле go_addr объявлено ;операторами dw
jmp go_addr Характеристики ячейки должны ;быть известны
Для дальнего косвенного перехода, как и для ближнего, допустима адресация через регистр общего назначения, если в него поместить адрес поля с адресом перехода:
mov BX,offset go_addr jmp [BX]
Возможно также использование базово-индексной адресации, если в сегменте данных имеется таблица с двухсловными адресами точек переходов. Циклы и условные переходы
Циклы, позволяют выполнить некоторый участок программы многократно, В системе команд МП 86 циклы реализуются с помощью команды loop (петля), Число шагов в цикле определяется содержимым регистра СХ. При реализации команды loop процессор сначала уменьшает содержимое регистра СХ на 1, а затем сравнивает полученное число с нулем. Если СХ > 0, переход на указанную метку выполняется. Если СХ = 0, цикл разрывается и процессор переходит на команду, следующую за командой loop. Поэтому после нормального выхода из цикла содержимое СХ всегда равно 0. В коде команды под смещение к точке перехода отводится всего 1 байт. Поскольку смещение должно являться величиной со знаком, максимальное расстояние, на которое можно передать управление командой loop, составляет от -128 до +127 байт (хотя довольно трудно представить себе цикл, в котором переход осуществляется вперед), таким образом, тело цикла ограничивается 128 байтами.
Рассмотрим простой пример организации цикла. Пусть в программе зарезервировано место для массива размером 10000 слов, и этот массив надо заполнить натуральным рядом чисел от 0 до 9999. Эти числа, заполняющие последовательные элементы массива, иногда называют числами-заполнителями. Соответствующий фрагмент программы будет выглядеть следующим образом:
;В сегменте данных
array dw 10000 dup(0)
;В программном сегменте
mov BX,offset array ;Адрес массива
mov SI, 0 ;Индекс
mov AX, 0 Начальное значение
;заполнителя mov CX,10000 ;Счетчик цикла
fill: mov [BX][SI],AX /Заполнитель пошлем в массив inc AX /Инкремент заполнителя
add SI,2 /Модификация индекса
loop fill /Команда цикла
В системе команд МП 86 имеется свыше трех десятков команд условных переходов, позволяющих осуществлять переходы при наличии разнообразных условий: равенства, неравенства, положительности или отрицательности результата и прочих. При выполнении всех этих команд процессор анализирует содержимое регистра флагов и осуществляет (или не осуществляет) переход на указанную метку, в зависимости от состояния отдельных флагов или их комбинаций.
В составе команд условных переходов имеются две группы команд для сравнения чисел без знака (это команды ja, jae, jb, jbe, jna, jnae, jnb и jnbe) и чисел со знаком (jg, jge, jl, jle, jiig, jnge, jnl и jnle). В аббревиатурах этих команд для сравнения чисел без знака используются слова above (выше) и below (ниже), а для чисел со знаком — слова greater (больше) и less (меньше).
Разница между теми и другими командами условных переходов заключается в том, что команды для чисел со знаком рассматривают понятия «больше-меньше» применительно к числовой оси -32К...0...+32К, а команды для чисел без знака — применительно к числовой оси 0...64К. Поэтому для первых команд число 7FFFh (+32767) больше числа 8000h (-32768), а для вторых число 7FFFh (32767) меньше числа 8000h (32768). Аналогично, команды для чисел со знаком считают, что 0 больше, чем . FFFFh (-1), а команды для чисел без знака — меньше.
Основная литература: 5[203-235],7 [74-82,123-134],9[63-82]
Дополнительная литература:18[ 49-55]
Контрольные вопросы:
1 .Содержимое каких регистров изменяют команды передачи управления?
2. На какие три группы по принципу действия можно разделить команды микропроцессора, обеспечивающие организацию переходов в программе?
3. В каких разновидностях осуществляются безусловные переходы с помощью команды jmp?
4. В каких случаях используется косвенный переход?
5. Содержимое какого регистра анализирует процессор при выполнении команд условной передачи управления.?