Ходьба

Команда безусловного перехода относится к арифметической операции. Безусловные переходы

Команда безусловного перехода относится к арифметической операции. Безусловные переходы

Арифметические команды рассматривают коды операндов как числовые двоичные или двоично-десятичные коды. Эти команды могут быть разделены на пять основных групп:

Команды операций с фиксированной запятой (сложение, вычитание, умножение, деление);

Команды операций с плавающей запятой (сложение, вычитание, умножение, деление);

Команды очистки;

Команды инкремента и декремента;

Команда сравнения.

Команды операций с фиксированной запятой работают с кодами в регистрах процессора или в памяти как с обычными двоичными кодами. Команда сложения (ADD) вычисляет сумму двух кодов. Команда вычитания (SUB) вычисляет разность двух кодов. Команда умножения (MUL) вычисляет произведение двух кодов (разрядность результата вдвое больше разрядности сомножителей). Команда деления (DIV) вычисляет частное от деления одного кода на другой. Причем все эти команды могут работать как с числами со знаком, так и с числами без знака.

Команды операций с плавающей запятой (точкой) используют формат представления чисел с порядком и мантиссой (обычно эти числа занимают две последовательные ячейки памяти). В современных мощных процессорах набор команд с плавающей запятой не ограничивается только четырьмя арифметическими действиями, а содержит и множество других более сложных команд, например, вычисление тригонометрических функций, логарифмических функций, а также сложных функций, необходимых при обработке звука и изображения.

Команды очистки (CLR) предназначены для записи нулевого кода в регистр или ячейку памяти. Эти команды могут быть заменены командами пересылки нулевого кода, но специальные команды очистки обычно выполняются быстрее, чем команды пересылки. Команды очистки иногда относят к группе логических команд, но суть их от этого не меняется.

Команды инкремента (увеличения на единицу, INC) и декремента (уменьшения на единицу, DEC) также бывают очень удобны. Их можно в принципе заменить командами суммирования с единицей или вычитания единицы, но инкремент и декремент выполняются быстрее, чем суммирование и вычитание. Эти команды требуют одного входного операнда, который одновременно является и выходным операндом.

Наконец, команда сравнения (обозначается CMP) предназначена для сравнения двух входных операндов. По сути, она вычисляет разность этих двух операндов, но выходного операнда не формирует, а всего лишь изменяет биты в регистре состояния процессора (PSW) по результату этого вычитания. Следующая за командой сравнения команда (обычно это команда перехода) будет анализировать биты в регистре состояния процессора и выполнять действия в зависимости от их значений. В некоторых процессорах предусмотрены команды цепочечного сравнения двух последовательностей операндов, находящихся в памяти (например, в процессоре 8086 и совместимых с ним).

Логические команды

Логические команды выполняют над операндами логические (побитовые) операции, то есть они рассматривают коды операндов не как единое число, а как набор отдельных битов. Этим они отличаются от арифметических команд. Логические команды выполняют следующие основные операции:

Логическое И, логическое ИЛИ, сложение по модулю 2 (Исключающее ИЛИ);

Логические, арифметические и циклические сдвиги;

Проверка битов и операндов;

Установка и очистка битов (флагов) регистра состояния процессора (PSW).

Команды логических операций позволяют побитно вычислять основные логические функции от двух входных операндов. Кроме того, операция И (AND) используется для принудительной очистки заданных битов (в качестве одного из операндов при этом используется код маски, в котором разряды, требующие очистки, установлены в нуль). Операция ИЛИ (OR) применяется для принудительной установки заданных битов (в качестве одного из операндов при этом используется код маски, в котором разряды, требующие установки в единицу, равны единице). Операция "Исключающее ИЛИ" (XOR) используется для инверсии заданных битов (в качестве одного из операндов при этом применяется код маски, в котором биты, подлежащие инверсии, установлены в единицу). Команды требуют двух входных операндов и формируют один выходной операнд.

Команды сдвигов позволяют побитно сдвигать код операнда вправо (в сторону младших разрядов) или влево (в сторону старших разрядов). Тип сдвига (логический, арифметический или циклический) определяет, каково будет новое значение старшего бита (при сдвиге вправо) или младшего бита (при сдвиге влево), а также определяет, будет ли где-то сохранено прежнее значение старшего бита (при сдвиге влево) или младшего бита (при сдвиге вправо). Например, при логическом сдвиге вправо в старшем разряде кода операнда устанавливается нуль, а младший разряд записывается в качестве флага переноса в регистр состояния процессора. А при арифметическом сдвиге вправо значение старшего разряда сохраняется прежним (нулем или единицей), младший разряд также записывается в качестве флага переноса.

Циклические сдвиги позволяют сдвигать биты кода операнда по кругу (по часовой стрелке при сдвиге вправо или против часовой стрелки при сдвиге влево). При этом в кольцо сдвига может входить или не входить флаг переноса. В бит флага переноса (если он используется) записывается значение старшего бита при циклическом сдвиге влево и младшего бита при циклическом сдвиге вправо. Соответственно, значение бита флага переноса будет переписываться в младший разряд при циклическом сдвиге влево и в старший разряд при циклическом сдвиге вправо.

Для примера на рис. 2.3 показаны действия, выполняемые командами сдвигов вправо.

Команды проверки битов и операндов предназначены для установки или очистки битов регистра состояния процессора в зависимости от значения выбранных битов или всего операнда в целом. Выходного операнда команды не формируют. Команда проверки операнда (TST) проверяет весь код операнда в целом на равенство нулю и на знак (на значение старшего бита), она требует только одного входного операнда. Команда проверки бита (BIT) проверяет только отдельные биты, для выбора которых в качестве второго операнда используется код маски. В коде маски проверяемым битам основного операнда должны соответствовать единичные разряды.

Рис. 2.3. Команды сдвигов вправо .

Наконец, команды установки и очистки битов регистра состояния процессора (то есть флагов) позволяют установить или очистить любой флаг, что бывает очень удобно. Каждому флагу обычно соответствуют две команды, одна из которых устанавливает его в единицу, а другая сбрасывает в нуль. Например, флагу переноса C (от Carry) будут соответствовать команды CLC (очистка) и SEC или STC (установка).

Команды переходов

Команды переходов предназначены для организации всевозможных циклов, ветвлений, вызовов подпрограмм и т.д., то есть они нарушают последовательный ход выполнения программы. Эти команды записывают в регистр-счетчик команд новое значение и тем самым вызывают переход процессора не к следующей по порядку команде, а к любой другой команде в памяти программ. Некоторые команды переходов предусматривают в дальнейшем возврат назад, в точку, из которой был сделан переход, другие не предусматривают этого. Если возврат предусмотрен, то текущие параметры процессора сохраняются в стеке. Если возврат не предусмотрен, то текущие параметры процессора не сохраняются.

Команды переходов без возврата делятся на две группы:

Команды безусловных переходов;

Команды условных переходов.

В обозначениях этих команд используются слова Branch (ветвление) и Jump (прыжок).

Команды безусловных переходов вызывают переход в новый адрес независимо ни от чего. Они могут вызывать переход на указанную величину смещения (вперед или назад) или же на указанный адрес памяти. Величина смещения или новое значение адреса указываются в качестве входного операнда.

Команды условных переходов вызывают переход не всегда, а только при выполнении заданных условий. В качестве таких условий обычно выступают значения флагов в регистре состояния процессора (PSW). То есть условием перехода является результат предыдущей операции, меняющей значения флагов. Всего таких условий перехода может быть от 4 до 16. Несколько примеров команд условных переходов:

Переход, если равно нулю;

Переход, если не равно нулю;

Переход, если есть переполнение;

Переход, если нет переполнения;

Переход, если больше нуля;

Переход, если меньше или равно нулю.

Если условие перехода выполняется, то производится загрузка в регистр-счетчик команд нового значения. Если же условие перехода не выполняется, счетчик команд просто наращивается, и процессор выбирает и выполняет следующую по порядку команду.

Специально для проверки условий перехода применяется команда сравнения (CMP), предшествующая команде условного перехода (или даже нескольким командам условных переходов). Но флаги могут устанавливаться и любой другой командой, например командой пересылки данных, любой арифметической или логической командой. Отметим, что сами команды переходов флаги не меняют, что как раз и позволяет ставить несколько команд переходов одну за другой.

Совместное использование нескольких команд условных и безусловных переходов позволяет процессору выполнять разветвленные алгоритмы любой сложности. Для примера на рис. 2.4 показано разветвление программы на две ветки с последующим соединением, а на рис. 2.5 - разветвление на три ветки с последующим соединением.

Команды переходов с дальнейшим возвратом в точку, из которой был произведен переход, применяются для выполнения подпрограмм, то есть вспомогательных программ. Эти команды называются также командами вызова подпрограмм (распространенное название - CALL). Использование подпрограмм позволяет упростить структуру основной программы, сделать ее более логичной, гибкой, легкой для написания и отладки. В то же время надо учитывать, что широкое использование подпрограмм, как правило, увеличивает время выполнения программы.

Рис. 2.4. Реализация разветвления на две ветки .

Рис. 2.5. Реализация разветвления на три ветки .

Все команды переходов с возвратом предполагают безусловный переход (они не проверяют никаких флагов). При этом они требуют одного входного операнда, который может указывать как абсолютное значение нового адреса, так и смещение, складываемое с текущим значением адреса. Текущее значение счетчика команд (текущий адрес) сохраняется перед выполнением перехода в стеке.

Для обратного возврата в точку вызова подпрограммы (точку перехода) используется специальная команда возврата (RET или RTS). Эта команда извлекает из стека значение адреса команды перехода и записывает его в регистр-счетчик команд.

Особое место среди команд перехода с возвратом занимают команды прерываний (распространенное название - INT). Эти команды в качестве входного операнда требуют номер прерывания (адрес вектора). Обслуживание таких переходов осуществляется точно так же, как и аппаратных прерываний. То есть для выполнения данного перехода процессор обращается к таблице векторов прерываний и получает из нее по номеру прерывания адрес памяти, в который ему необходимо перейти. Адрес вызова прерывания и содержимое регистра состояния процессора (PSW) сохраняются в стеке. Сохранение PSW - важное отличие команд прерывания от команд переходов с возвратом.

Команды прерываний во многих случаях оказываются удобнее, чем обычные команды переходов с возвратом. Сформировать таблицу векторов прерываний можно один раз, а потом уже обращаться к ней по мере необходимости. Номер прерывания соответствует номеру подпрограммы, то есть номеру функции, выполняемой подпрограммой. Поэтому команды прерывания гораздо чаще включаются в системы команд процессоров, чем обычные команды переходов с возвратом.

Для возврата из подпрограммы, вызванной командой прерывания, используется команда возврата из прерывания (IRET или RTI). Эта команда извлекает из стека сохраненное там значение счетчика команд и регистра состояния процессора (PSW).

Отметим, что у некоторых процессоров предусмотрены также команды условных прерываний, например, команда прерывания при переполнении.

Конечно, в данном разделе мы рассмотрели только основные команды, наиболее часто встречающиеся в процессорах. У конкретных процессоров могут быть и многие другие команды, не относящиеся к перечисленным группам команд. Но изучать их надо уже после того, как выбран тип процессора, подходящий для задачи, решаемой данной микропроцессорной системой.


Предыдущее обсуждение выявило некоторые детали механизма перехода. Команды перехода модифицируют регистр указателя команды EIP/IP и, возможно, сегментный регистр кода CS. Что именно должно подвергнуться модификации, зависит:

от типа операнда в команде безусловного перехода (ближний или дальний);

от модификатора, который указывается перед адресом перехода в команде перехода и может принимать следующие значения (сам адрес при прямом переходе находится непосредственно в команде, а при косвенном - в регистре или ячейке памяти):

NEAR PTR - прямой переход на метку внутри текущего сегмента кода, при этом модифицируется только регистр EIP/IP (в зависимости от заданного типа сегмента кода use16 или use32) на основе указанного в команде адреса (метки) или выражения, использующего символ извлечения значения счетчика адреса команд ($);

FAR PTR - прямой переход на метку в другом сегменте кода, при этом адрес перехода задается в виде непосредственного операнда или адреса (метки) и состоит из 16-разрядного селектора и 16/32-разрядного смещения, которые загружаются, соответственно, в регистры CS и EIP/IP;

WORD PTR - косвенный переход на метку внутри текущего сегмента кода, при этом модифицируется (значением смещения размером 16 или 32 бита из памяти по указанному в команде адресу или из регистра) только регистр EIP/IP;

DWORD PTR - косвенный переход на метку в другом сегменте кода, при этом модифицируются (значением из памяти - и только из памяти, из регистра нельзя) оба регистра, CS и EIP/IP (первое слово/двойное слово адреса перехода, представляющее собой смещение, загружается в EIP/IP; второе/третье слово - в CS).

Команда безусловного перехода

Синтаксис команды безусловного перехода без сохранения информации о точке

возврата:

jmp [модификатор] адрес_перехода

Здесь адрес_перехода представляет метку или адрес области памяти, в которой находится указатель перехода.

Всего в системе команд процессора есть несколько кодов машинных команд безусловного перехода JMP. Их различия определяются дальностью перехода и способом задания целевого адреса. Дальность перехода определяется местоположением операнда адрес_перехода. Этот адрес может находиться в текущем сегменте кода или в некотором другом сегменте. В первом случае переход называется внутрисегментным, или близким, во втором - межсегментным, или дальним.

Внутрисегментный переход предполагает, что изменяется только содержимое регистра EIP/IP. Можно выделить три варианта внутрисегментного использования команды JMP:

Прямой короткий переход;

Прямой переход;

Косвенный переход.

Прямой короткий внутрисегментный переход применяется, когда расстояние от команды JMP до адреса перехода не превышает -128 или +127 байт. В этом случае транслятор ассемблера формирует машинную команду безусловного перехода длиной всего два байта (размер обычной команды внутрисегментного безусловного перехода составляет три байта). Первый байт в этой команде - код операции, значение которого говорит о том, что процессор должен особым образом трактовать второй байт команды. Значение второго байта вычисляется транслятором как разность между значением смещения команды, следующей за JMP, и значением адреса перехода. При осуществлении прямого короткого перехода нужно иметь в виду следующий важный момент, связанный с местоположением адреса перехода и самой команды JMP. Если адрес перехода расположен до команды JMP, то ассемблер формирует короткую команду безусловного перехода без дополнительных указаний. Если адрес перехода расположен после команды JMP, транслятор не может сам определить, что переход короткий, так как у него еще нет информации об адресе перехода. Для оказания помощи компилятору в формировании команды короткого безусловного перехода в дополнение к ранее упомянутым модификаторам используют модификатор SHORT PTR:

jmp short ptr ml

... ;не более 35-40 команд (127 байт)

Еще вариант:

...;не более 35-40 команд (-128 байт)

Прямой внутрисегментный переход отличается от прямого короткого внутрисегментного перехода тем, что длина машинной команды JMP в этом случае составляет три байта. Увеличение длины связано с тем, что поле адреса перехода в машинной команде JMP расширяется до двух байтов, а это, в свою очередь, позволяет производить переходы в пределах 64 Кбайт относительно следующей за JMP команды:

Расстояние более 128 байт и менее 64 Кбайт

Косвенный внутрисегментный переход подразумевает «косвенность» задания адреса перехода. Это означает, что в команде указывается не сам адрес перехода, а место, где он «лежит». Приведем несколько примеров, в которых двухбайтовый адрес перехода выбирается либо из регистра, либо из области памяти:

jmp bx ;адрес перехода в регистре bх

jmp addr_ml ;адрес перехода в ячейке памяти addrjnl

Еще несколько вариантов косвенного внутрисегментного перехода:

<3>addr dw ml

<10>jmpaddr;адрес перехода в слове памятиaddr+(si)

<12>mov si.2

<13>jmp cycl

В этом примере одна команда JMP (строка 10) может выполнять переходы на разные метки. Выбор конкретной метки перехода определяется содержимым регистра SI. Операнд команды JMP определяет адрес перехода косвенно после вычисления выражения addr+(SI).

<3>addr dw ml

<7> lea si ,addr

<8> jmp near ptr ;адрес перехода в ячейке памяти addr

В данном случае указание модификатора NEAR PTR обязательно, так как, в отличие от предыдущего способа, адрес ячейки памяти addr с адресом перехода транслятору передается неявно (строки 3, 7 и 8), и, не имея информации о метке, он не может определить, какой именно переход осуществляется - внутрисегментный или межсегментный. Межсегментный переход предполагает другой формат машинной команды JM Р. При осуществлении межсегментного перехода кроме регистра EIP/IP

модифицируется также регистр CS. Аналогично внутрисегментному переходу, межсегментный переход поддерживают два варианта команд безусловного перехода:

прямой и косвенный.

Команда прямого межсегментного перехода имеет длину пять байтов, из которых два байта составляют значение смещения и два байта - значение сегментной составляющей адреса:

jmp far ptr m2 ;здесь far обязательно

jmp ml ;здесь far необязательно

Рассматривая этот пример, обратите внимание на модификатор FAR PTR в команде JMP. Его необходимость объясняется все той же логикой работы однопроходного транслятора. Если описание метки (метка ml) встречается в исходном тексте программы раньше, чем соответствующая ей команда перехода, то задание модификатора необязательно, так как транслятор все знает о данной метке и сам формирует нужную пятибайтовую форму команды безусловного перехода. В случае, когда команда перехода встречается до описания соответствующей метки, транслятор не имеет еще никакой информации о метке, и модификатор FAR PTR в команде JMP опускать нельзя, так как транслятор не знает, какую форму команды формировать - трехбайтную или пятибайтную. Без специального указания модификатора транслятор будет формировать трехбайтную команду внутрисегментного перехода.

Команда косвенного межсегментного перехода в качестве операнда имеет адрес области памяти, в которой содержатся смещение и сегментная часть целевого адреса перехода:

addrjnl dd ml ;в поле addr_ml значения смещения

;и адреса сегмента метки ml

Как вариант косвенного межсегментного перехода необходимо отметить косвенный регистровый межсегментный переход. В этом виде перехода адрес перехода указывается косвенно - в регистре. Это очень удобно для программирования динамических переходов, в которых адрес перехода может изменяться на стадии выполнения программы:

addr_ml dd ml ;в поле addr_ml значения смещения

;и адреса сегмента метки ml

jmp dword ptr

Таким образом, модификаторы SHORT PTR, NEAR PTR и WORD PTR применяются для организации внутрисегментных переходов, a FAR PTR и DWORD PTR - межсегментных.

Для полной ясности нужно еще раз подчеркнуть, что если тип сегмента - use32, то в тех местах, где речь шла о регистре IP, можно использовать регистр EIP и, соответственно, размеры полей смещения увеличить до 32 битов.

логическое ИЛИ , сложение по модулю 2 (Исключающее ИЛИ);
  • логические, арифметические и циклические сдвиги;
  • проверка битов и операндов;
  • установка и очистка битов (флагов) регистра состояния процессора ( PSW ).
  • Команды логических операций позволяют побитно вычислять основные логические функции от двух входных операндов. Кроме того, операция И (AND ) используется для принудительной очистки заданных битов (в качестве одного из операндов при этом используется код маски, в котором разряды, требующие очистки, установлены в нуль). Операция ИЛИ (OR ) применяется для принудительной установки заданных битов (в качестве одного из операндов при этом используется код маски, в котором разряды, требующие установки в единицу, равны единице). Операция "Исключающее ИЛИ" (XOR ) используется для инверсии заданных битов (в качестве одного из операндов при этом применяется код маски, в котором биты, подлежащие инверсии, установлены в единицу). Команды требуют двух входных операндов и формируют один выходной операнд.

    Команды сдвигов позволяют побитно сдвигать код операнда вправо (в сторону младших разрядов) или влево (в сторону старших разрядов). Тип сдвига (логический, арифметический или циклический) определяет, каково будет новое значение старшего бита (при сдвиге вправо) или младшего бита (при сдвиге влево), а также определяет, будет ли где-то сохранено прежнее значение старшего бита (при сдвиге влево) или младшего бита (при сдвиге вправо). Например, при логическом сдвиге вправо в старшем разряде кода операнда устанавливается нуль, а младший разряд записывается в качестве флага переноса в регистр состояния процессора. А при арифметическом сдвиге вправо значение старшего разряда сохраняется прежним (нулем или единицей), младший разряд также записывается в качестве флага переноса.

    Циклические сдвиги позволяют сдвигать биты кода операнда по кругу (по часовой стрелке при сдвиге вправо или против часовой стрелки при сдвиге влево). При этом в кольцо сдвига может входить или не входить флаг переноса. В бит флага переноса (если он используется) записывается значение старшего бита при циклическом сдвиге влево и младшего бита при циклическом сдвиге вправо. Соответственно, значение бита флага переноса будет переписываться в младший разряд при циклическом сдвиге влево и в старший разряд при циклическом сдвиге вправо.

    Для примера на рис. 3.12 показаны действия, выполняемые командами сдвигов вправо.

    Команды проверки битов и операндов предназначены для установки или очистки битов регистра состояния процессора в зависимости от значения выбранных битов или всего операнда в целом. Выходного операнда команды не формируют. Команда проверки операнда ( TST ) проверяет весь код операнда в целом на равенство нулю и на знак (на значение старшего бита), она требует только одного входного операнда. Команда проверки бита (BIT ) проверяет только отдельные биты, для выбора которых в качестве второго операнда используется код маски. В коде маски проверяемым битам основного операнда должны соответствовать единичные разряды.


    Рис. 3.12.

    Наконец, команды установки и очистки битов регистра состояния процессора (то есть флагов) позволяют установить или очистить любой флаг, что бывает очень удобно. Каждому флагу обычно соответствуют две команды, одна из которых устанавливает его в единицу, а другая сбрасывает в нуль. Например, флагу переноса C (от Carry ) будут соответствовать команды CLC (очистка) и SEC или STC (установка).

    3.3.4. Команды переходов

    Команды переходов предназначены для организации всевозможных циклов, ветвлений , вызовов подпрограмм и т.д., то есть они нарушают последовательный ход выполнения программы. Эти команды записывают в регистр- счетчик команд новое значение и тем самым вызывают переход процессора не к следующей по порядку команде, а к любой другой команде в памяти программ. Некоторые команды переходов предусматривают в дальнейшем возврат назад, в точку, из которой был сделан переход, другие не предусматривают этого. Если возврат предусмотрен, то текущие параметры процессора сохраняются в стеке. Если возврат не предусмотрен, то текущие параметры процессора не сохраняются.

    Команды переходов без возврата делятся на две группы:

    • команды безусловных переходов ;
    • команды условных переходов .

    В обозначениях этих команд используются слова Branch (ветвление ) и Jump (прыжок).

    Команды безусловных переходов вызывают переход в новый адрес независимо ни от чего. Они могут вызывать переход на указанную величину смещения (вперед или назад) или же на указанный адрес памяти. Величина смещения или новое значение адреса указываются в качестве входного операнда.

    Команды условных переходов вызывают переход не всегда, а только при выполнении заданных условий. В качестве таких условий обычно выступают значения флагов в регистре состояния процессора ( PSW ). То есть условием перехода является результат предыдущей операции, меняющей значения флагов. Всего таких условий перехода может быть от 4 до 16. Несколько примеров команд условных переходов :

    • переход, если равно нулю;
    • переход, если не равно нулю;
    • переход, если есть переполнение;
    • переход, если нет переполнения;
    • переход, если больше нуля;
    • переход, если меньше или равно нулю.
    нового значения. Если же условие перехода не выполняется, счетчик команд просто наращивается, и процессор выбирает и выполняет следующую по порядку команду.

    Специально для проверки условий перехода применяется команда сравнения ( CMP ), предшествующая команде условного перехода (или даже нескольким командам условных переходов ). Но флаги могут устанавливаться и любой другой командой, например командой пересылки данных, любой арифметической или логической командой . Отметим, что сами команды переходов флаги не меняют, что как раз и позволяет ставить несколько команд переходов одну за другой.

    Совместное использование нескольких команд условных и безусловных переходов позволяет процессору выполнять разветвленные алгоритмы любой сложности. Для примера на рис. 3.13 показано разветвление программы на две ветки с последующим соединением, а на рис. 3.14 - разветвление на три ветки с последующим соединением.

    Команды переходов с дальнейшим возвратом в точку, из которой был произведен переход, применяются для выполнения подпрограмм , то есть вспомогательных программ. Эти команды называются также командами вызова подпрограмм (распространенное название - CALL ). Использование подпрограмм позволяет упростить структуру основной программы, сделать ее более логичной, гибкой, легкой для написания и отладки. В то же время надо учитывать, что широкое использование подпрограмм , как правило, увеличивает время выполнения программы.


    Рис. 3.13.


    Рис. 3.14.

    Все команды переходов с возвратом предполагают безусловный переход (они не проверяют никаких флагов). При этом они требуют одного входного операнда, который может указывать как абсолютное значение нового адреса, так и смещение, складываемое с текущим значением адреса. Текущее значение счетчика команд (текущий адрес) сохраняется перед выполнением перехода в стеке.

    Для обратного возврата в точку вызова

    Наряду со средствами арифметических вычислений, система команд микропроцессора имеет также средства логического преобразования данных. Под логическими понимаются такие преобразования данных, в основе которых лежат правила формальной логики.

    Формальная логика работает на уровне утверждений истинно и ложно. Для микропроцессора это, как правило, означает 1 и 0 соответственно. Для компьютера язык нулей и единиц является родным, но минимальной единицей данных, с которой работают машинные команды, является байт. Однако на системном уровне часто необходимо иметь возможность работать на предельно низком уровне – на уровне бит.

    Рис. 29. Средства логической обработки данных


    К средствам логического преобразования данных относятся логические команды и логические операции. Операнд команды ассемблера в общем случае может представлять собой выражение, которое, в свою очередь, является комбинаций операторов и операндов. Среди этих операторов могут быть и операторы, реализующие логические операции над объектами выражения.

    Перед подробным рассмотрением этих средств рассмотрим, что же представляют собой сами логические данные и какие операции над ними производятся.

    Логические данные

    Теоретической базой для логической обработки данных является формальная логика. Существует несколько систем логики. Одна из наиболее известных – это исчисление высказываний. Высказывание – это любое утверждение, о котором можно сказать, что оно либо истинно, либо ложно.

    Исчисление высказываний представляет собой совокупность правил, используемых для определения истинности или ложности некоторой комбинации высказываний.

    Исчисление высказываний очень гармонично сочетается с принципами работы компьютера и основными методами его программирования. Все аппаратные компоненты компьютера построены на логических микросхемах. Система представления информации в компьютере на самом нижнем уровне основана на понятии бита. Бит, имея всего два состояния (0 (ложно) и 1 (истинно)), естественным образом вписывается в исчисление высказываний.

    Согласно теории, над высказываниями (над битами) могут выполняться следующие логические операции.


    1. Отрицание (логическое НЕ) – логическая операция над одним операндом, результатом которой является величина, обратная значению исходного операнда.

    Эта операция однозначно характеризуется следующей таблицей истинности (табл. 12).

    Таблица 12. Таблица истинности для логического отрицания

    2. Логическое сложение (логическое включающее ИЛИ) – логическая операция над двумя операндами, результатом которой является «истина» (1), если один или оба операнда имеют значение «истина» (1), и «ложь» (0), если оба операнда имеют значение «ложь» (0).

    Эта операция описывается с помощью следующей таблицы истинности (табл. 13).

    Таблица 13. Таблица истинности для логического включающего ИЛИ

    3. Логическое умножение (логическое И) – логическая операция над двумя операндами, результатом которой является «истина» (1) только в том случае, если оба операнда имеют значение «истина» (1). Во всех остальных случаях значение операции «ложь» (0).

    Эта операция описывается с помощью следующей таблицы истинности (табл. 14).

    Таблица 14. Таблица истинности для логического И

    4. Логическое исключающее сложение (логическое исключающее ИЛИ) – логическая операция над двумя операндами, результатом которой является «истина» (1), если только один из двух операндов имеет значение «истина» (1), и ложь (0), если оба операнда имеют значение «ложь» (0) или «истина» (1). Эта операция описывается с помощью следующей таблицы истинности (таб. 15).

    Таблица 15. Таблица истинности для логического исключающего ИЛИ

    Система команд микропроцессора содержит пять команд, поддерживающих данные операции. Эти команды выполняют логические операции над битами операндов. Размерность операндов, естественно, должна быть одинакова. Например, если размерность операндов равна слову (16 бит), то логическая операция выполняется сначала над нулевыми битами операндов, и ее результат записывается на место бита 0 результата. Далее команда последовательно повторяет эти действия над всеми битами с первого до пятнадцатого.

    Логические команды

    В системе команд микропроцессора есть следующий набор команд, поддерживающих работу с логическими данными:

    1) and операнд_1, операнд_2 – операция логического умножения. Команда выполняет поразрядно логическую операцию И (конъюнкцию) над битами операндов операнд_1 и операнд_2. Результат записывается на место операнд_1;

    2) ог операнд_1, операнд_2 – операция логического сложения. Команда выполняет поразрядно логическую операцию ИЛИ (дизъюнкцию) над битами операндов операнд_1 и операнд_2. Результат записывается на место операнд_1;

    3) хог операнд_1, операнд_2 – операция логического исключающего сложения. Команда выполняет поразрядно логическую операцию исключающего ИЛИ над битами операндов операнд_1 и операнд_2. Результат записывается на место операнде;

    4) test операнд_1, операнд_2 – операция «проверить» (способом логического умножения). Команда выполняет поразрядно логическую операцию И над битами операндов операнд_1 и операнд_2. Состояние операндов остается прежним, изменяются только флаги zf, sf, и pf, что дает возможность анализировать состояние отдельных битов операнда без изменения их состояния;

    5) not операнд – операция логического отрицания. Команда выполняет поразрядное инвертирование (замену значения на обратное) каждого бита операнда. Результат записывается на место операнда.

    Для представления роли логических команд в системе команд микропроцессора очень важно понять области их применения и типовые приемы их использования при программировании.

    С помощью логических команд возможно выделение отдельных битов в операнде с целью их установки, сброса, инвертирования или просто проверки на определенное значение.

    Для организации подобной работы с битами операнд_2 обычно играет роль маски. С помощью установленных в 1 бите этой маски и определяются нужные для конкретной операции биты операнд_1. Покажем, какие логические команды могут применяться для этой цели:

    1) для установки определенных разрядов (бит) в 1 применяется команда ог операнд_1, операнд_2.

    В этой команде операнд_2, выполняющий роль маски, должен содержать единичные биты на месте тех разрядов, которые должны быть установлены в 1 в операнд_1;

    2) для сброса определенных разрядов (бит) в 0 применяется команда and операнд_1, операнд_2.

    В этой команде операнд_2, выполняющий роль маски, должен содержать нулевые биты на месте тех разрядов, которые должны быть установлены в 0 в операнд_1;

    3) команда хог операнд_1, операнд_2 применяется:

    а) для выяснения того, какие биты в операнд_1 и операнде различаются;

    б) для инвертирования состояния заданных бит в операнд_1.

    Интересующие нас биты маски (операнд_2) при выполнении команды хог должны быть единичными, остальные – нулевыми;

    Для проверки состояния заданных бит применяется команда test операнд_1, операнд_2 (проверить операнд_1).

    Проверяемые биты операнд_1 в маске (операнд_2) должны иметь единичное значение. Алгоритм работы команды test подобен алгоритму команды and, но он не меняет значения операнд_1. Результатом команды является установка значения флага нуля zf:

    1) если zf = 0, то в результате логического умножения получился нулевой результат, т. е. один единичный бит маски, который не совпал с соответствующим единичным битом операнде;

    2) если zf = 1, то в результате логического умножения получился ненулевой результат, т. е. хотя бы один единичный бит маски совпал с соответствующим единичным битом операнд_1.

    Для реакции на результат команды test целесообразно использовать команду перехода jnz метка (Jump if Not Zero) – переход, если флаг нуля zf ненулевой, или команду с обратным действием – jz метка (Jump if Zero) – переход, если флаг нуля zf = 0.

    Следующие две команды позволяют осуществить поиск первого установленного в 1 бита операнда. Поиск можно произвести как с начала, так и от конца операнда:

    1) bsf операнд_1, операнд_2 (Bit Scaning Forward) – сканирование битов вперед. Команда просматривает (сканирует) биты операнд_2 от младшего к старшему (от бита 0 до старшего бита) в поисках первого бита, установленного в 1. Если таковой обнаруживается, в операнд_1 заносится номер этого бита в виде целочисленного значения. Если все биты операнд_2 равны 0, то флаг нуля zf устанавливается в 1, в противном случае флаг zf сбрасывается в 0;

    2) bsr операнд_1, операнд_2 (Bit Scaning Reset) – сканирование битов в обратном порядке. Команда просматривает (сканирует) биты операнд_2 от старшего к младшему (от старшего бита к биту 0) в поисках первого бита, установленного в 1. Если таковой обнаруживается, в операнд_1 заносится номер этого бита в виде целочисленного значения. При этом важно, что позиция первого единичного бита слева отсчитывается все равно относительно бита 0. Если все биты операнд_2 равны 0, то флаг нуля zf устанавливается в 1, в противном случае флаг zf сбрасывается в 0.

    В последних моделях микропроцессоров Intel в группе логических команд появилось еще несколько команд, которые позволяют осуществить доступ к одному конкретному биту операнда. Операнд может находиться как в памяти, так и в регистре общего назначения. Положение бита задается смещением бита относительно младшего бита операнда. Значение смещения может задаваться как в виде непосредственного значения, так и содержаться в регистре общего назначения. В качестве значения смещения вы можете использовать результаты работы команд bsr и bsf. Все команды присваивают значение выбранного бита флагу СЕ

    1) bt операнд, смещение_бита (Bit Test) – проверка бита. Команда переносит значение бита в флаг cf;

    2) bts операнд, смещение_бита (Bit Test and Set) – проверка и установка бита. Команда переносит значение бита в флаг CF и затем устанавливает проверяемый бит в 1;

    3) btr операвд, смещение_бита (Bit Test and Reset) – проверка и сброс бита. Команда переносит значение бита в флаг CF и затем устанавливает этот бит в 0;

    4) btc операнд, смещение_бита (Bit Test and Convert) – проверка и инвертирование бита. Команда переносит значение бита в флаг cf и затем инвертирует значение этого бита.

    Команды сдвига

    Команды этой группы также обеспечивают манипуляции над отдельными битами операндов, но иным способом, чем логические команды, рассмотренные выше.

    Все команды сдвига перемещают биты в поле операнда влево или вправо в зависимости от кода операции. Все команды сдвига имеют одинаковую структуру – коп операнд, счетчик_сдвигов.

    Количество сдвигаемых разрядов – счетчик_сдвигов – располагается на месте второго операнда и может задаваться двумя способами:

    1) статически, что предполагает задание фиксированного значения с помощью непосредственного операнда;

    2) динамически, что означает занесение значения счетчика сдвигов в регистр cl перед выполнением команды сдвига.

    Исходя из размерности регистра cl понятно, что значение счетчика сдвигов может лежать в диапазоне от 0 до 255. Но на самом деле это не совсем так. В целях оптимизации микропроцессор воспринимает только значение пяти младших битов счетчика, т. е. значение лежит в диапазоне от 0 до 31.

    Все команды сдвига устанавливают флаг переноса cf.

    По мере сдвига битов за пределы операнда они сначала попадают на флаг переноса, устанавливая его равным значению очередного бита, оказавшегося за пределами операнда. Куда этот бит попадет дальше, зависит от типа команды сдвига и алгоритма программы.

    По принципу действия команды сдвига можно разделить на два типа:

    1) команды линейного сдвига;

    2) команды циклического сдвига.

    Команды линейного сдвига

    К командам этого типа относятся команды, осуществляющие сдвиг по следующему алгоритму:

    1) очередной «выдвигаемый» бит устанавливает флаг CF;

    2) бит, вводимый в операнд с другого конца, имеет значение 0;

    3) при сдвиге очередного бита он переходит во флаг CF, при этом значение предыдущего сдвинутого бита теряется! Команды линейного сдвига делятся на два подтипа:

    1) команды логического линейного сдвига;

    2) команды арифметического линейного сдвига.

    К командам логического линейного сдвига относятся следующие:

    1) shl операнд, счетчик_сдвигов (Shift Logical Left) – логический сдвиг влево. Содержимое операнда сдвигается влево на количество битов, определяемое значением счетчик_сдвигов. Справа (в позицию младшего бита) вписываются нули;

    2) shr операнд, счетчик_сдвигов (Shift Logical Right) – логический сдвиг вправо. Содержимое операнда сдвигается вправо на количество битов, определяемое значением счетчик_сдвигов. Слева (в позицию старшего, знакового бита) вписываются нули.

    На рисунке 30 показан принцип работы этих команд.

    Рис. 30. Схема работы команд линейного логического сдвига


    Команды арифметического линейного сдвига отличаются от команд логического сдвига тем, что они особым образом работаются со знаковым разрядом операнда.

    1) sal операнд, счетчик_сдвигов (Shift Arithmetic Left) – арифметический сдвиг влево. Содержимое операнда сдвигается влево на количество битов, определяемое значением счетчик_сдвигов. Справа (в позицию младшего бита) вписываются нули. Команда sal не сохраняет знака, но устанавливает флаг с/в случае смены знака очередным выдвигаемым битом. В остальном команда sal полностью аналогична команде shl;

    2) sar операнд, счетчик_сдвигов (Shift Arithmetic Right) – арифметический сдвиг вправо. Содержимое операнда сдвигается вправо на количество битов, определяемое значением счетчик_сдвигов. Слева в операнд вписываются нули. Команда sar сохраняет знак, восстанавливая его после сдвига каждого очередного бита.

    На рисунке 31 показан принцип работы команд линейного арифметического сдвига.


    Рис. 31. Схема работы команд линейного арифметического сдвига

    Команды циклического сдвига

    К командам циклического сдвига относятся команды, сохраняющие значения сдвигаемых бит. Есть два типа команд циклического сдвига:

    1) команды простого циклического сдвига;

    2) команды циклического сдвига через флаг переноса cf.

    К командам простого циклического сдвига относятся:

    1) rol операнд, счетчик_сдвигов (Rotate Left) – циклический сдвиг влево. Содержимое операнда сдвигается влево на количество бит, определяемое операндом счетчик_сдвигов. Сдвигаемые влево биты записываются в тот же операнд справа;

    2) гог операнд, счетчик_сдвигов (Rotate Right) – циклический сдвиг вправо. Содержимое операнда сдвигается вправо на количество бит, определяемое операндом счетчик_сдвигов. Сдвигаемые вправо биты записываются в тот же операнд слева.

    Рис. 32. Схема работы команд простого циклического сдвига


    Как видно из рисунка 32, команды простого циклического сдвига в процессе своей работы осуществляют одно полезное действие, а именно: циклически сдвигаемый бит не только вдвигается в операнд с другого конца, но и одновременно его значение становится значением флага СЕ

    Команды циклического сдвига через флаг переноса CF отли чаются от команд простого циклического сдвига тем, что сдвигаемый бит не сразу попадает в операнд с другого его конца, а записывается сначала в флаг переноса СЕ Лишь следующее исполнение данной команды сдвига (при условии, что она выполняется в цикле) приводит к помещению выдвинутого ранее бита с другого конца операнда (рис. 33).

    К командам циклического сдвига через флаг переноса соотносятся следующие:

    1) rcl операнд, счетчик_сдвигов (Rotate through Carry Left) – циклический сдвиг влево через перенос.

    Содержимое операнда сдвигается влево на количество бит, определяемое операндом счетчик_сдвигов. Сдвигаемые биты поочередно становятся значением флага переноса cf.

    2) гсг операнд, счетчик_сдвигов (Rotate through Carry Right) – циклический сдвиг вправо через перенос.

    Содержимое операнда сдвигается вправо на количество бит, определяемое операндом счетчик_сдвигов. Сдвигаемые биты поочередно становятся значением флага переноса СF.

    Рис. 33. Команды циклического сдвига через флаг переноса CF


    Из рисунка 33 видно, что при сдвиге через флаг переноса появляется промежуточный элемент, с помощью которого, в частности, можно производить подмену циклически сдвигаемых битов, в частности, рассогласование битовых последовательностей.

    Под рассогласованием битовой последовательности здесь и далее подразумевается действие, которое позволяет некоторым образом локализовать и извлечь нужные участки этой последовательности и записать их в другое место.

    Дополнительные команды сдвига

    Система команд последних моделей микропроцессоров Intel, начиная с i80386, содержит дополнительные команды сдвига, расширяющие возможности, рассмотренные нами ранее. Это – команды сдвигов двойной точности: 1) shld операнд_1, операнд_2, счетчик_сдвигов – сдвиг влево двойной точности. Команда shld производит замену путем сдвига битов операнда операнд_1 влево, заполняя его биты справа значениями битов, вытесняемых из операнд_2 согласно схеме на рис. 34. Количество сдвигаемых бит определяется значением счетчик_сдвигов, операнд_2 не изменяется.


    Рис. 34. Схема работы команды shld


    2) shrd операнд_1, операнд_2, счетчик_сдвигов – сдвиг вправо двойной точности. Команда производит замену путем сдвига битов операнда операнд_1 вправо, заполняя его биты слева значениями битов, вытесняемых из операнд_2 согласно схеме на рисунке 35. Количество сдвигаемых бит определяется значением счетчик_сдвигов, которое может лежать в диапазоне 0… 31. Это значение может задаваться непосредственным операндом или содержаться в регистре cl. Значение операнд_2 не изменяется.

    Рис. 35. Схема работы команды shrd


    Как мы отметили, команды shld и shrd осуществляют сдвиги до 32 разрядов, но за счет особенностей задания операндов и алгоритма работы эти команды можно использовать для работы с полями длиной до 64 бит.

    2. Команды передачи управления

    Мы познакомились с некоторыми командами, из которых формируются линейные участки программы. Каждая из них в общем случае выполняет некоторые действия по преобразованию или пересылке данных, после чего микропроцессор передает управление следующей команде. Но очень мало программ работает таким последовательным образом. Обычно в программе есть точки, в которых нужно принять решение о том, какая команда будет выполняться следующей. Это решение может быть:

    1) безусловным – в данной точке необходимо передать управление не той команде, которая идет следующей, а другой, которая находится на некотором удалении от текущей команды;

    2) условным – решение о том, какая команда будет выполняться следующей, принимается на основе анализа некоторых условий или данных.

    Программа представляет собой последовательность команд и данных, занимающих определенное пространство оперативной памяти. Это пространство памяти может быть либо непрерывным, либо состоять из нескольких фрагментов.

    То, какая команда программы должна выполняться следующей, микропроцессор узнает по содержимому пары регистров cs:(e)ip:

    1) cs – сегментный регистр кода, в котором находится физический (базовый) адрес текущего сегмента кода;

    2) eip/ip – регистр указателя команды, в котором находится значение, представляющее собой смещение в памяти следующей команды, подлежащей выполнению, относительно начала текущего сегмента кода.

    Какой конкретно регистр будет использоваться, зависит от установленного режима адресации use16 или use32. Если указано use 16, то используется ip, если use32, то используется eip.

    Таким образом, команды передачи управления изменяют содержимое регистров cs и eip/ip, в результате чего микропроцессор выбирает для выполнения не следующую по порядку команду программы, а команду в некотором другом участке программы. Конвейер внутри микропроцессора при этом сбрасывается.

    По принципу действия команды микропроцессора, обеспечивающие организацию переходов в программе, можно разделить на 3 группы:

    1. Команды безусловной передачи управления:

    1) команда безусловного перехода;

    2) команда вызова процедуры и возврата из процедуры;

    3) команда вызова программных прерываний и возврата из программных прерываний.

    2. Команды условной передачи управления:

    1) команды перехода по результату команды сравнения стр;

    2) команды перехода по состоянию определенного флага;

    3) команды перехода по содержимому регистра есх/сх.

    3. Команды управления циклом:

    1) команда организации цикла со счетчиком есх/сх;

    2) команда организации цикла со счетчиком есх/сх с возможностью досрочного выхода из цикла по дополнительному условию.

    Безусловные переходы

    Предыдущее обсуждение выявило некоторые детали механизма перехода. Команды перехода модифицируют регистр указателя команды eip/ip и, возможно, сегментный регистр кода cs. Что именно должно подвергнуться модификации, зависит:

    1) от типа операнда в команде безусловного перехода (ближний или дальний);

    2) от указания перед адресом перехода (в команде перехода) модификатора; при этом сам адрес перехода может находиться либо непосредственно в команде (прямой переход), либо в регистре или ячейке памяти (косвенный переход).

    Модификатор может принимать следующие значения:

    1) near ptr – прямой переход на метку внутри текущего сегмента кода. Модифицируется только регистр eip/ip (в зависимости от заданного типа сегмента кода use16 или use32) на основе указанного в команде адреса (метки) или выражения, использующего символ извлечения значения – $;

    2) far ptr – прямой переход на метку в другом сегменте кода. Адрес перехода задается в виде непосредственного операнда или адреса (метки) и состоит из 16-битного селектора и 16/32-битного смещения, которые загружаются, соответственно, в регистры cs и ip/eip;

    3) word ptr – косвенный переход на метку внутри текущего сегмента кода. Модифицируется (значением смещения из памяти по указанному в команде адресу, или из регистра) только eip/ip. Размер смещения 16 или 32 бит;

    4) dword ptr – косвенный переход на метку в другом сегменте кода. Модифицируются (значением из памяти – и только из памяти, из регистра нельзя) оба регистра – cs и eip/ip. Первое слово/двойное слово этого адреса представляет смещение и загружается в ip/eip; второе/третье слово загружается в cs. Команда безусловного перехода jmp

    Синтаксис команды безусловного перехода – jmp [модификатор] адрес_перехода – безусловный переход без сохранения информации о точке возврата.

    Адрес_перехода представляет собой адрес в виде метки либо адрес области памяти, в которой находится указатель перехода.

    Всего в системе команд микропроцессора есть несколько кодов машинных команд безусловного перехода jmp.

    Их различия определяются дальностью перехода и способом задания целевого адреса. Дальность перехода определяется местоположением операнда адрес_перехода. Этот адрес может находиться в текущем сегменте кода или в некотором другом сегменте. В первом случае переход называется внутрисегментным, или близким, во втором – межсегментным, или дальним. Внутрисегментный переход предполагает, что изменяется только содержимое регистра eip/ip.

    Можно выделить три варианта внутрисегментного использования команды jmp:

    1) прямой короткий;

    2) прямой;

    3) косвенный.


    Процедуры

    В языке ассемблера есть несколько средств, решающих проблему дублирования участков программного кода. К ним относятся:

    1) механизм процедур;

    2) макроассемблер;

    3) механизм прерываний.

    Процедура, часто называемая также подпрограммой, – это основная функциональная единица декомпозиции (разделения на несколько частей) некоторой задачи. Процедура представляет собой группу команд для решения конкретной подзадачи и обладает средствами получения управления из точки вызова задачи более высокого уровня и возврата управления в эту точку.

    В простейшем случае программа может состоять из одной процедуры. Другими словами, процедуру можно определить как правильным образом оформленную совокупность команд, которая, будучи однократно описана, при необходимости может быть вызвана в любом месте программы.

    Для описания последовательности команд в виде процедуры в языке ассемблера используются две директивы: PROC и ENDP.

    Синтаксис описания процедуры таков (рис. 36).


    Рис. 36. Синтаксис описания процедуры в программе


    Из рисунка 36 видно, что в заголовке процедуры (директиве PROC) обязательным является только задание имени процедуры. Среди большого количества операндов директивы PROC следует особо выделить [расстояние]. Этот атрибут может принимать значения near или far и характеризует возможность обращения к процедуре из другого сегмента кода. По умолчанию атрибут [расстояние] принимает значение near.

    Процедура может размещаться в любом месте программы, но так, чтобы на нее случайным образом не попало управление. Если процедуру просто вставить в общий поток команд, то микропроцессор будет воспринимать команды процедуры как часть этого потока и, соответственно, будет осуществлять выполнение команд процедуры.

    Условные переходы

    Микропроцессор имеет 18 команд условного перехода. Эти команды позволяют проверить:

    1) отношение между операндами со знаком («больше – меньше»);

    2) отношение между операндами без знака («выше – ниже»);

    3) состояния арифметических флагов ZF, SF, CF, OF, PF (но не AF).

    Команды условного перехода имеют одинаковый синтаксис:

    jcc метка_перехода

    Как видно, мнемокод всех команд начинается с «j» – от слова jump (прыжок), ее – определяет конкретное условие, анализируемое командой.

    Что касается операнда метка_перехода, то эта метка может находиться только в пределах текущего сегмента кода, межсегментная передача управления в условных переходах не допускается. В связи с этим отпадает вопрос о модификаторе, который присутствовал в синтаксисе команд безусловного перехода. В ранних моделях микропроцессора (i8086, i80186 и i80286) команды условного перехода могли осуществлять только короткие переходы – на расстояние от -128 до +127 байт от команды, следующей за командой условного перехода. Начиная с модели микропроцессора 80386, это ограничение снято, но, как видите, только в пределах текущего сегмента кода.

    Для того чтобы принять решение о том, куда будет передано управление командой условного перехода, предварительно должно быть сформировано условие, на основании которого и будет приниматься решение о передаче управления.

    Источниками такого условия могут быть:

    1) любая команда, изменяющая состояние арифметических флагов;

    2) команда сравнения стр, сравнивающая значения двух операндов;

    3) состояние регистра есх/сх.


    Команда сравнения cmp

    Команда сравнения стр имеет интересный принцип работы. Он абсолютно такой же, как и у команды вычитания – sub операнде, операнд_2.

    Команда стр так же, как и команда sub, выполняет вычитание операндов и устанавливает флаги. Единственное, чего она не делает – это запись результата вычитания на место первого операнда.

    Синтаксис команды стр – стр операнд_1, операнд_2 (compare) – сравнивает два операнда и по результатам сравнения устанавливает флаги.

    Флаги, устанавливаемые командой стр, можно анализировать специальными командами условного перехода. Прежде чем мы их рассмотрим, уделим немного внимания мнемонике этих команд условного перехода (табл. 16). Понимание обозначений при формировании названия команд условного перехода (элемент в названии команды jcc, обозначенный нами ее) облегчит их запоминание и дальнейшее практическое использование.

    Таблица 16. Значение аббревиатур в названии команды jcc
    Таблица 17. Перечень команд условного перехода для команды стр операнд_1, операнд_2

    He удивляйтесь тому обстоятельству, что одинаковым значениям флагов соответствует несколько разных мнемокодов команд условного перехода (они отделены друг от друга косой чертой в табл. 17). Разница в названии обусловлена желанием разработчиков микропроцессора облегчить использование команд условного перехода в сочетании с определенными группами команд. Поэтому разные названия отражают скорее различную функциональную направленность. Тем не менее то, что эти команды реагируют на одни и те же флаги, делает их абсолютно эквивалентными и равноправными в программе. Поэтому в таблице 17 они сгруппированы не по названиям, а по значениям флагов (условиям), на которые они реагируют.


    Команды условного перехода и флаги

    Мнемоническое обозначение некоторых команд условного перехода отражает название флага, с которым они работают, и имеет следующую структуру: первым идет символ «j» (Jump, переход), вторым – либо обозначение флага, либо символ отрицания «n», после которого стоит название флага. Такая структура команды отражает ее назначение. Если символа «n» нет, то проверяется состояние флага, если он равен 1, производится переход на метку перехода. Если символ «n» присутствует, то проверяется состояние флага на равенство 0, и в случае успеха производится переход на метку перехода.

    Мнемокоды команд, названия флагов и условия переходов приведены в таблице 18. Эти команды можно использовать после любых команд, изменяющих указанные флаги.

    Таблица 18. Команды условного перехода и флаги

    Если внимательно посмотреть на таблицы 17 и 18, видно, что многие команды условного перехода в них являются эквивалентными, так как в основе и тех, и других лежит анализ одинаковых флагов.


    Команды условного перехода и регистр есх/сх

    Архитектура микропроцессора предполагает специфическое использование многих регистров. К примеру, регистр EAX/AX/AL используется как аккумулятор, а регистры ВР, SP – для работы со стеком. Регистр ЕСХ/СХ тоже имеет определенное функциональное назначение: он выполняет роль счетчика в командах управления циклами и при работе с цепочками символов. Возможно, что функционально команду условного перехода, связанную с регистром есх/сх, правильнее было бы отнести к этой группе команд.

    Синтаксис этой команды условного перехода таков:

    1) jcxz метка_перехода (Jump if ex is Zero) – переход, если сх нуль;

    2) jecxz метка_перехода (Jump Equal есх Zero) – переход, если есх нуль.

    Эти команды очень удобно использовать при организации цикла и при работе с цепочками символов.

    Нужно отметить ограничение, свойственное команде jcxz/jecxz. В отличие от других команд условной передачи управления команда jcxz/jecxz может адресовать только короткие переходы – на -128 байт или на +127 байт от следующей за ней команды.

    Организация циклов

    Цикл, как известно, представляет собой важную алгоритмическую структуру, без использования которой не обходится, наверное, ни одна программа. Организовать циклическое выполнение некоторого участка программы можно, к примеру, используя команды условной передачи управления или команду безусловного перехода jmp. При такой организации цикла все операции по его организации выполняются вручную. Но, учитывая важность такого алгоритмического элемента, как цикл, разработчики микропроцессора ввели в систему команд группу из трех команд, облегчающую программирование циклов. Эти команды также используют регистр есх/сх как счетчик цикла.

    Дадим краткую характеристику этим командам: 1) loop метка_перехода (Loop) – повторить цикл. Команда позволяет организовать циклы, подобные циклам for в языках высокого уровня с автоматическим уменьшением счетчика цикла. Работа команды заключается в выполнении следующих действий:

    б) сравнения регистра ЕСХ/СХ с нулем: если (ЕСХ/СХ) = 0, то управление передается на следующую после loop команду;

    2) loope/loopz метка_перехода

    Команды loope и loopz – абсолютные синонимы. Работа команд заключается в выполнении следующих действий:

    а) декремента регистра ЕСХ/СХ;

    в) анализа состояния флага нуля ZF если (ЕСХ/СХ) = 0 или XF = 0, управление передается на следующую после loop команду.

    3) loopne/loopnz метка_перехода

    Команды loopne и loopnz также абсолютные синонимы. Работа команд заключается в выполнении следующих действий:

    а) декремента регистра ЕСХ/СХ;

    б) сравнения регистра ЕСХ/СХ с нулем;

    в) анализа состояния флага нуля ZF: если (ЕСХ/СХ) = 0 или ZF = 1, управление передается на следующую после loop команду.

    Команды loope/loopz и loopne/loopnz по принципу своей работы являются взаимообратными. Они расширяют действие команды loop тем, что дополнительно анализируют флаг zf, что дает возможность организовать досрочный выход из цикла, используя этот флаг в качестве индикатора.

    Недостаток команд организации цикла loop, loope/loopz и loopne/loopnz состоит в том, что они реализуют только короткие переходы (от -128 до +127 байт). Для работы с длинными циклами придется использовать команды условного перехода и команду jmp, поэтому постарайтесь освоить оба способа организации циклов.

    К группе команд безусловной передачи управления относятся команды соб- ственно безусловного перехода, команды перехода к подпрограмме (вызова под- программ) и команды возврата из подпрограмм.

    Команда безусловного перехода имеет уже упоминавшийся мнемокод jmр.

    Для организации безусловного перехода в регистры сs и iр (или только iр) загру- жается их новое содержимое, то есть новый указатель. При этом их старое содер- жимое - старый указатель - безвозвратно теряется. Это значит, что после со- вершения перехода восстановить точку программы, из которой он был совершен, невозможно. На рис. 4.36, а приведен пример выполнения безусловного перехо- да по команде jmр Ml.


    Вызов подпрограмм осуществляется с помощью команды с мнемокодом call (вызвать). В команду call входит метка директивы рrос (от procedure - про- цедура), с которой в языке Ассемблер принято начинать подпрограммы. Закан- чиваются подпрограммы директивой епdр (от end procedure - конец процеду- ры), которая должна иметь ту же самую метку, что и директива рrос.

    Обращение к подпрограмме происходит следующим образом. Вначале старое значение указателя из регистров сs:iр помещается в стек (рис. 4.36, б). Отме- тим, что в момент выполнения команды call регистры сs:iр содержат адрес команды, которая расположена непосредственно за этой командой вызова под- программы. На рис. 4.36, б эта точка программы отмечена меткой VI. Затем из команды вызова выбирается новый указатель (в примере на рисунке это указа- тель на метку Р1 начала подпрограммы), который загружается в регистры сs:iр. Тем самым обеспечивается переход к выполнению вызванной подпрограммы.

    Сохранение в стеке адреса, откуда был совершен переход в подпрограмму, обес- печивает возможность при необходимости вернуться в точку вызова. Чтобы та- кой возврат произошел, в подпрограмму должна быть включена команда rеt (от return - возврат), схема выполнения которой показана на рис. 4.36, в. По коман- де rеt производятся выборка указателя из вершины стека и последующая его загрузка в пару регистров сs:iр. В примере на рис. 4.36 из стека выбирается
    указатель, соответствующий метке VI, которой отмечена команда, непосредст- венно следующая за командой вызова call Рl.



    Использование стека для хранения последовательности адресов возвратов позво- ляет правильно организовать вложенные обращения к подпрограммам. На рис. 4.37 изображена схема выполнения вложенных вызовов, когда из основной програм- мы вызывается подпрограмма Рl, а из нее - другая подпрограмма Р2. Во время выполнения первого вызова по команде call Рl (рис. 4.37, а) в стек попадает адрес точки возврата VI и управление передается в начало подпрограммы Рl. Внутри подпрограммы Рl вызывается другая подпрограмма Р2. По команде call Р2 в стек попадает адрес второй точки возврата V2 и происходит переход в начало второй подпрограммы Р 2 (рис. 4.37, б).

    Предполагается, что в теле подпрограммы Р2 других вызовов нет. Поэтому в мо- мент выполнения находящейся в теле подпрограммы Р2 команды rеt из верши- ны стека выбирается адрес второй точки возврата V2 (рис. 4.37, в) и таким обра- зом осуществляется возвращение в первую подпрограмму Рl, в ту ее точку, из которой была вызвана подпрограмма Р2.

    Когда закончится выполнение подпрограммы Рl и начнется выполнение ее ко- манды rеt (рис. 4.37, г), из вершины стека окажется выбранным адрес первой точки возврата VI и, следовательно, осуществится возврат в основную программу.

    Обсуждаемая схема обеспечивает корректную обработку вызовов подпрограмм с произвольной глубиной вложенности. Если быть точным, то глубина вложен- ности вызовов ограничивается только объемом стека.