Глава 11 — команды сравнения и передачи управления
Команда сравнения CMP сравнивает два числа, вычитая второе из первого, также как и команда SUB. Отличие команд CMP и SUB состоит в том, что инструкция CMP не сохраняет результат, а лишь устанавливает в соответствии с результатом флаги состояния. Основное назначение команды CMP – это организация ветвлений (условных переходов) в ассемблерных программах.
Безусловный переход – это переход, который передает управление без сохранения информации возврата всякий раз, когда выполняется. Ему соответствует команда JMP. Эта команда может осуществлять переход вплоть до 32768 байт. Если заранее известно, что переход вперед делается на место, лежащее в диапазоне 128 байт от текущего места, можно использовать команду JMP SHORT LABEL. Атрибут SHORT заставляет Ассемблер сформировать короткую форму команды перехода, даже если он еще не встретил метку LABEL.
Условный переход проверяет текущее состояние машины (флагов или регистров), чтобы определить, передать управление или нет.
1) проверяющие результаты предыдущей арифметической или логической операции Jcc;
2) управляющие итерациями фрагмента программы (организация циклов) LOOPcc.
Все условные переходы имеют однобайтовое смещение, то есть метка, на которую происходит переход должна находится в том же кодовом сегменте и на расстоянии, не превышающем –128 +127 байт от первого байта следующей команды. Если условный переход осуществляется на место, находящееся дальше 128 байт, то вместо недопустимой команды
JZ ZERO
необходимо использовать специальные конструкции типа:
JNZ CONTINUE
JMP ZERO
CONTINUE:
Первая группа команд Jcc (кроме JCXZ/JECXZ) проверяет текущее состояние регистра флагов (не изменяя его) и в случае соблюдения условия осуществляет переход на смещение, указанное в качестве операнда. Флаги, проверяемые командой, кодируются в ее мнемонике, например: JC – переход, если установлен CF. Сокращения «L» (less – меньше) и «G» (greater – больше) применяются для целых со знаком, а «A» (above – над) и «B» (below – под) для целых без знака.
Ниже в таблице показаны команды условного перехода и проверяемые ими флаги.
Буква Х в любой позиции означает, что команда не проверяет флаг. Цифра 0 означает, что флаг должен быть сброшен, а цифра 1 означает, что флаг должен быть установлен, чтобы условие было выполнено (переход произошел).
Команды условного перехода можно разделить на три подгруппы:
1) Непосредственно проверяющие один из флагов на равенство 0 или 1.
2) Арифметические сравнения со знаком. Существуют 4 условия, которые могут быть проверены: меньше (JL), меньше или равно (JLE), больше (JG), больше или равно (JGE). Эти команды проверяют одновременно три флага: знака, переполнения и нуля.
3) Арифметические без знака. Здесь также существует 4 возможных соотношения между операндами. Учитываются только два флага. Флаг переноса показывает какое из двух чисел больше. Флаг нуля определяет равенство.
Ниже приведен фрагмент программы, иллюстрирующий использование команд сравнения и перехода.
MOV BH,X ;Загрузка в BH значения Х
MOV BL,Y ;Загрузка в BL значения Y
CMP BH,BL ;Сравнение BH и BL
JE MET1 ;Если BH=BL, то переход на MET1
JMP MET2 ;Иначе переход на MET2
MET1:
…
JMP MET3
MET2:
…
MET3:
MOV AH,4Ch
INT 21H
Есть еще команда JCXZ, она отличается от других команд условного перехода тем, что она проверяет содержимое регистра CX, а не флагов. Эту команду лучше всего применять в начале условного цикла, чтобы предотвратить вхождение в цикл, если CX=0.
Вторая группа команд условного перехода — LOOPcc служит для организации циклов в программах. Все команды цикла используют регистр CX в качестве счетчика цикла. Простейшая из них – команда LOOP. Она уменьшает содержимое CX на 1 и передает управление на указанную метку, если содержимое CX не равно 0. Если вычитание 1 из CX привело к нулевому результату, выполняется команда, следующая за LOOP.
Команда LOOPNE (цикл пока не равно) осуществляет выход из цикла, если установлен флаг нуля или если регистр CX достиг нуля. Команда LOOPE (цикл пока равно) выполняет обратную к описанной выше проверку флага нуля: в этом случае цикл завершается, если регистр CX достиг нуля или если не установлен флаг нуля.
Приведенный ниже фрагмент программы иллюстрирует использование команд организации циклов.
BUF DB “0123406789”
……………………………..
MOV CX,10 ;В CX – длинна буфера
MOV SI,0
M1: MOV DL,[BX+SI] ;В DL – символ из буфера
MOV AH,2 ;в AH номер функции-вывода (для int)
INT 21H ;Вывод на экран — функция OS
INC SI ;Увеличение индекса на 1
LOOP M1 ;Оператор цикла
Команда JMP
Что такое JavaScript
Если вы интересуетесь программированием вообще, и сайтостроением в частности, то вы наверняка слышали слово JavaScript. И, если вы до сих пор не узнали толком, что же это такое, то пришло время сделать это. Подробнее… |
Синтаксис команды JMP такой:
JMP МЕТКА
МЕТКОЙ может быть один из следующих:
- Идентификатор метки в исходном коде
- Адрес в формате СЕГМЕНТ:СМЕЩЕНИЕ
Безусловный переход в Ассемблере выполняется всегда и в любом случае, если в исходном коде имеется инструкция JMP.
Никакие флаги при выполнении этой инструкции не изменяются.
Что такое безусловный переход
Как следует из названия, это переход без условий. Это значит, что если в исходном коде встречается команда JMP, то эта команда выполняет переход на указанный в МЕТКЕ адрес без каких либо условий — в любом случае.
Что такое метка в Ассемблере
Теперь немного поговорим о том, что такое метка в Ассемблере.
Метка — это идентификатор, после которого следует двоеточие. Идентификатор должен быть составлен по правилам составления идентификаторов для конкретного языка программирования
Пример метки в Ассемблере:
--- Здесь инструкции Ассемблера --- MyLabel: ; Это МЕТКА --- И здесь инструкции Ассемблера ---
Для чего нужны метки?
Для того, чтобы управлять ходом выполнения программы.
Например, в зависимости от результата выполнения какой-то команды, вам требуется направить ход выполнения программы по одному из двух путей. То есть в зависимости от результата выполнить один из двух участков кода.
Тогда каждый участок кода обозначается своей меткой. И таким образом вы будете иметь возможность пропустить какой-то участок кода и сразу перейти к другому участку.
Более подробно об этом мы будем говорить при изучении инструкций условного перехода. Эта же статья посвящена безусловному переходу, то есть когда надо просто перейти к нужному участку кода без каких либо условий.
В языках высокого уровня подобные действия называются ветвлением, и реализуются с помощью соответствующих конструкций языка (таких как if…then…else или case в Паскале).
Пример безусловного перехода
Сначала давайте подумаем, зачем нужен безусловный переход.
Логичный вопрос: если мы всегда и во всех случаях пропускаем участок кода, то зачем нам тогда вообще этот участок?
Вопрос закономерный, но только для новичка. На самом деле бывают разные ситуации, когда этот переход нужен.
JMP Label_2 Label_1: --- Участок кода 1 --- Label_2: ; Это МЕТКА --- Участок кода 2 --- JMP Label_1
Здесь при первом проходе программы мы пропускаем участок кода 1 и сразу переходим к метке Label_2 (то есть ко второму участку). Но во втором участке мы возвращаемся к участку 1 (к метке Label_1) и выполняем его.
Пример второй: может быть так, что какой-то участок кода требуется только для отладки. Тогда можно сделать так:
;JMP Label_2 --- Участок кода ТОЛЬКО для отладки --- Label_2: ; Это МЕТКА --- Участок кода 2 ---
Обратите внимание, что инструкция JMP закомментирована, то есть не выполняется. Так что пока вы отлаживаете свою программу, участок кода для отладки выполняется.
Когда отладка программы закончена, мы убираем точку с запятой перед инструкцией JMP и отладочный код выполняться не будет, так как будет выполняться безусловный переход на метку Label_2.
А почему бы просто не убрать отладочный код?
Можно, конечно, и убрать. Но вы уверены, что он вам больше не пригодится?
В конце как обычно расскажу, почему эта команда ассемблера называется JMP. Это сокращение от английского слова JUMP, которое можно перевести как “прыжок, переход”.
Первые шаги в программирование
Главный вопрос начинающего программиста – с чего начать? Вроде бы есть желание, но иногда «не знаешь, как начать думать, чтобы до такого додуматься». У человека, который никогда не имел дело с информационными технологиями, даже простые вопросы могут вызвать большие трудности и отнять много времени на решение. Подробнее… |
Untitled
UntitledБезусловные переход — это такой переход, который передает
управление всякий раз, когда он выполняется. Наоборот, услловный
переход проверяет текущее состояние машины, чтобы определить,
передавать управление или нет. Существует два вида команд
безусловной передачи управления — команды переходов и вызовов.
Все команды вызова 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,
который существует в некоторых языках высокого уровня.
Управление ходом программы
Короткие условные переходыПодобно команде JMP, которая выполняет безусловный переход, существуют команды, которые осуществляют условный переход (переход, который осуществляется только в том случае, если выполняется определенное условие). Эти команды разделяются на три группы. Первая группа только проверяет отдельный флаг, вторая — сравнивает числа со знаком, третья — сравнивает числа без знака.
Команды перехода, проверяющие одиночный флаг
Команда | Описание | Условие | Обратная команда |
JZ , JE | Переход, если «равно» («нуль»). Т.е. если сравниваемые значения равны, то ZF = 1 и переход выполняется | ZF = 1 | JNZ, JNE |
JC , JB, JNAE | Переход, если есть перенос («ниже», «не выше или равно»). | CF = 1 | JNC, JNB, JAE |
JS | Переход по знаку. | SF = 1 | JNS |
JO | Переход по переполнению. | OF = 1 | JNO |
JPE, JP | Переход, если есть паритет или паритет четный. | PF = 1 | JPO |
JNZ , JNE | Переход по «не равно» или по «не нуль» | ZF = 0 | JZ, JE |
JNC , JNB, JAE | Переход, если нет переноса («выше или равно» или «не ниже»). | CF = 0 | JC, JB, JNAE |
JNS | Переход, если нет знака. | SF = 0 | JS |
JNO | Переход, если нет переполнения. | OF = 0 | JO |
JPO, JNP | Переход, если нет паритета или паритет нечетный. | PF = 0 | JPE, JP |
Как вы можете видеть, существуют команды, которые выполняют
одинаковые действия. Это нормально. Они даже ассемблируются в
одинаковый машинный код, поэтому будет неплохо, если вы запомните,
что при компиляции команды JE, после дизассемблирования
вы получите ее как: JZ.
Различные имена используются для того, чтобы делать программы
более легкими для понимания и кодирования.
Команды перехода для чисел со знаками
Команда | Описание | Условие | Обратная команда |
JE , JZ | Переход, если «равно» (=). переход, если «ноль». | ZF = 1 | JNE, JNZ |
JNE , JNZ | Переход, если «не равно» (). Переход, если «не ноль». | ZF = 0 | JE, JZ |
JG , JNLE | Переход, если «больше» (>). Переход, если «не меньше или равно» (not <=). | ZF = 0 and SF = OF | JNG, JLE |
JL , JNGE | Переход, если «меньше» (). Переход, если «не больше или равно» (not >=). | SF <> OF | JNL, JGE |
JGE , JNL | Переход, если «больше или равно» (>=). Переход, если «не меньше» (not <). | SF = OF | JNGE, JL |
JLE , JNG | Переход, если «меньше или равно» (<=). Переход, если «не больше» (not >). | ZF = 1 or SF OF | JNLE, JG |
<> — этот знак означает «не равно».
Команды перехода для чисел без знаков
Команда | Описание | Условие | Обратная команда |
JE , JZ | Переход, если «равно» (=). Переход, если «ноль». | ZF = 1 | JNE, JNZ |
JNE , JNZ | Переход, если «не равно» (). Переход, если «не ноль». | ZF = 0 | JE, JZ |
JA , JNBE | Переход, если «выше» (>). Переход, если «не ниже или равно» (not <=). | CF = 0 and ZF = 0 | JNA, JBE |
JB , JNAE, JC | Переход, если «ниже» (<). Переход, если «не выше или равно» (not >=). Переход по переносу. | CF = 1 | JNB, JAE, JNC |
JAE , JNB, JNC | Переход, если «выше или равно» (>=). Переход, если «не ниже» (not <). Переход, если «нет переноса». | CF = 0 | JNAE, JB |
JBE , JNA | Переход, если «ниже или равно» (<=). Переход, если «не выше» (not >). | CF = 1 or ZF = 1 | JNBE, JA |
Обычно, если требуется сравнить два числовых значения, то используют команду CMP (она делает то же самое, что и команда SUB (вычитание), но не сохраняет результат, а влияет только на флаги.
Логика очень простая, например:
требуется сравнить числа 5 и 2,
5 — 2 = 3
результат — НЕ НОЛЬ (Флаг Нуля — Zero Flag (ZF) установлен в 0).
Другой пример:
требуется сравнить 7 и 7,
7 — 7 = 0
результат — НОЛЬ! (Флаг Нуля — Zero Flag (ZF) установлен в 1 и команды JZ или JE выполнят переход).
Ниже приведен пример команды CMP и условного перехода:
include emu8086.inc ORG 100h MOV AL, 25 ; записать в AL число 25. MOV BL, 10 ; записать в BL число 10. CMP AL, BL ; сравнить AL с BL. JE equal ; если AL = BL (ZF = 1), то перейти к метке equal. PUTC 'N' ; иначе, если AL <> BL, то продолжить выполнение JMP stop ; программы - напечатать 'N' и перейти к метке stop. equal: ; если программа на этой метке, PUTC 'Y' ; то AL = BL, поэтому выводим на экран 'Y'. stop: RET ; сюда приходим в любом случае END |
Попробуйте вышеописанный пример с различными числами в AL и BL, откройте флаги, щелкнув по кнопке [FLAGS]. Используйте [Single Step — пошаговый режим] и наблюдайте за происходящим. Не забывайте перекомпилировать и перезагружать вашу программу после кажого сделанного вами в ней изменения (используйте клавишу F5).
Все условные переходы имеют одно серьезное ограничение — в отличие от команды JMP, они могут выполнять переход только на 127 байтов вперед или на 128 байтов назад (учтите, что большие команды ассемблируются в 3 и более байтов).
Мы можем легко преодолеть это ограничение, используя следующий метод:
- Взять обратную команду из приведенной выше таблицы и выполнить переход к метке label_x.
- Использовать команду JMP для перехода к нужному участку программы.
- Определить метку label_x: только после команды JMP.
Пример:
include emu8086.inc ORG 100h MOV AL, 25 ; записать в AL число 25. MOV BL, 10 ; записать в BL число 10. CMP AL, BL ; сравнить AL с BL. JNE not_equal ; переход, если AL <> BL (ZF = 0). JMP equal not_equal: ; представим, что здесь у нас ; размещается код, который ассмеблируется ; более чем в 127 байтов... PUTC 'N' ; если мы оказались здесь, то AL <> BL, JMP stop ; тогда печатаем 'N', и переходим к метке stop. equal: ; если мы оказались здесь, PUTC 'Y' ; то AL = BL, тогда печатаем 'Y'. stop: RET ; сюда приходим в любом случае. END |
Команды перехода — Ветвление и управление циклами
Несмотря на то что мы ввели несколько команд для выполнения условного перехода, на самом деле существует всего лишь одна. Все известные нам на настоящий момент команды являются фактически лишь формами записи команды ВС (Branch on Condition—УСЛОВНЫЙ ПЕРЕХОД) формата RX. Такие расширенные мнемоники, как В, BZ, BNM, транслируются ассемблером в машинную форму команды ВС; при этом попутно в формат команды вносится информация о том, какому состоянию признака результата соответствует действительный переход.
Маска
Состояние признака результата, которому соответствует переход, должно быть известно CPU при выполнении команд условной передачи управления. Информация об этом состоянии предоставляется процессору в виде маски. Вообще говоря, маска — это двоичное число, определяющее, какие из возможных логических решений должны быть приняты. Можно представить себе маску как набор переключателей. Если переключатель находится во включенном состоянии — соответствующий разряд маски содержит 1, то решение, соответствующее этому биту, должно быть принято. Если переключатель поставлен в положение «выключено» (разряд маски содержит 0), то выполнения соответствующих действий не требуется.
Одним из операндов команды ВС является 4-битовая маска. Мы можем считать, что разряды маски перенумерованы от 0 до 3. Каждый бит маски соответствует одному из четырех возможных состояний признака результата:
По маске определяется, какие значения признака результата вызывают выполнение перехода. Например, если при выполнении команды ВС 2-й разряд маски содержит 1, то переход будет выполнен, если признак результата имеет значение 2. Если данный бит содержитО, то наличие соответствующего признака результата не вызовет перехода.
Рассмотрим команду ВС с маской 01002. Так как бит 1 установлен в 1, то передача управления произойдет, если признак результата равен 012. Биты 0, 2 и 3 — нули, поэтому при значениях 0, 2 или 3 признака результата переход не будет иметь места. Так как код 1 признака результата соответствует отрицательному результату операции, команда ВС с такой маской эквивалентна описанной ранее команде ВМ.
Если несколько битов маски содержат 1, переход осуществляется, если выполнено хотя бы одно из соответствующих условий. Если маска содержит 10112, то переход осуществится, если признак результата будет иметь одно из значений 0, 2 или 3, но не 1. Команде ВС с такой маской отвечает расширенная мнемоника BNM.
Аналогично по команде ВС с маской 11112 производится переход независимо от признака результата. Эта команда имеет расширенную мнемонику В, что обозначает безусловный переход.
При выполнении команды ВС значение признака результата используется в качестве индекса, указывающего, какой разряд маски подлежит проверке. Если в этом разряде маски содержится 1, то осуществляется переход.
Таким образом, маска является связующим звеном между программистом и машиной. С ее помощью программист указывает условия, при выполнении которых он желал бы выполнить переход. Для задания этих условий используются расширенные мнемоники, транслируемые ассемблером в команды ВС с требуемой маской. Можно также задавать маску непосредственно, как мы увидим в следующем разделе.
Команда BC
Команда ВС представляет собой команду модифицированного формата RX. Мы уже отмечали, что 4-битовая маска, указывающая в каких случаях следует осуществить переход, должна быть задана в качестве одного из операндов. Символически эта маска обозначается Mlи помещается на место поля R1 команды обычного формата RX. Итак, команду ВС можно описать так:
ВС Ml ,D2(X2,B2) |
Branch on Condition |
PCD2 + (X2)+(B2), если признак результата соответствует значению в поле M1 |
Отметим, что мы использовали явную форму указания адреса памяти, на который следует передать управление, задав смещение D2, базовый и индексный регистры В2 и Х2. В будущем при указании операндов, хранящихся в памяти, мы будем употреблять лишь явную форму. Имейте в виду, что там, где использована явная спецификация, может быть применена и неявная, путем задания символического имени и выражения. Например, команда ВС может иметь и такой вид:
ВС M1,S2(X2)
Поскольку, если нет специальных оговорок, ассемблер рассматривает все числа как десятичные, маску можно определять десятичным числом от 0 до 15 включительно. Для определения, что именно нужно написать в поле Ml, двоичное значение маски следует перевести в десятичную форму. Если мы хотим, например, задать маску 1 ООО2, указывая тем самым, что переход необходимо осуществлять, если признак результата равен 0, нужно написать
ВС 8, LOOP
(переход при этом будет осуществляться на команду с меткой LOOP). Если же нам требуется передать управление по адресу LOOP независимо от значения признака результата, то в качестве маски нужно задать
1111,=1510
и команда примет вид
ВС 15, LOOP
Перевод маски в десятичную форму, несмотря на свою простоту, часто является источником дополнительных ошибок. Ассемблер позволяет задавать числа в различных системах. В данном случае, как и в некоторых других, можно использовать системы с иными основаниями.
Для определения двоичной константы достаточно написать
В’значение’
«Значение» — это двоичное число, заключенное в одиночные кавычки. Символ В сообщает ассемблеру, что основанием используемой системы является число 2.
Шестнадцатеричная константа задается так
X’значение’
При необходимости эти возможности можно использовать для определения числовых констант. Например, следующие команды эквивалентны:
ВС 13,LOOP
ВС В’1101′,LOOP
ВС X’D’,LOOP
BNP LOOP
Машинный формат команды ВС ничем не отличается от обычного формата RX, лишь на месте первого операнда указывается маска. Команда ВС всего одна, кодом соответствующей операции является 47, поэтому в машинной форме
ВС M1,D2(X2,B2)
выглядит так:
Вам должен быть знаком этот вид: последние 2 бита представляют S-адресное поле, оно вместе с полубайтом, задающим номер индекс-регистра, формирует Х-поле. 4-битовое же поле, обычно занимаемое номером регистра-операнда, теперь содержит маску. Оттранслируем вручную команду
BNP 32(3,11)
В данном случае смещение равно 3210, регистр 3 используется в качестве индексного, а регистр 11 — в качестве базового. Расширенная мнемоника BNP транслируется в команду ВС с маской 13 следующего вида:
ВС 13,32(3,11)
или
ВС X ‘D ‘,32(3,11)
Теперь, с учетом преобразования смещения в шестнадцатеричную форму, получим следующий машинный код:
47D3B020
Рассмотрим теперь другой пример. Оттранслируем команду
BZ ТОР
Имени ТОР соответствует смещение 15016, регистр 9 используется в качестве базового. Маской является 1000а=816. Таким образом, в машинном коде данная команда выглядит так:
47809150
При обработке команд передачи управления ассемблер выполняет аналогичные действия. Если написана команда ВС, то выполняется непосредственная трансляция. В случае же использования расширенной мнемоники ассемблеру приходится просматривать таблицу мнемоник для определения маски. После этого код 47 и полученная маска вносятся в соответствующие поля машинной команды.
Таблица 9.1 Расширенные мнемоники, маски и эквивалентные команды ВС
Расширенные мнемоники |
Значение |
Маска(двоичная) |
Маска(десятичная) |
Эквивалентные команды ВС суказанием маски |
|
В |
Branch (unconditional) |
1111 |
15 |
ВС |
15 |
NOP |
No Operation |
0000 |
0 |
ВС |
0 |
BZ |
Branch on Zero |
1000 |
8 |
ВС |
8 |
BE |
Branch on. Equal |
1000 |
8 |
ВС |
8 |
ВМ |
Branch on Minus |
0100 |
4 |
ВС |
4 |
BL |
Branch on Low |
0100 |
4 |
ВС |
4 |
ВР |
Branch on Plus |
0010 |
2 |
ВС |
2 |
BH |
Branch on High |
0010 |
2 |
ВС |
2 |
BO |
Branch on Overflow |
0001 |
1 |
ВС |
1 |
BNZ |
Branch on Not Zero |
0111 |
7 |
ВС |
7 |
BNE |
Branch on Not Equal |
0111 |
7 |
ВС |
7 |
BNM |
Branch on Not Minus |
1011 |
11 |
ВС |
11 |
BNL |
Branch on Not Low |
1011 |
И |
ВС |
11 |
BNP |
Branch on Not Plus |
1101 |
13 |
ВС |
13 |
BNH |
Branch on Not High |
1101 |
13 |
ВС |
13 |
BNO |
Branch on Not Overflow |
1110 |
14 |
BC |
14 |
В табл. 9. 1 приведены все расширенные мнемоники, обрабатываемые ассемблером F OS/360. До настоящего момента мы рассматривали получение признака результата только как результат выполнения некоторых арифметических операций. Впоследствии мы увидим, что признак результата вырабатывается и в результате выполнения логических операций (команд, выполняющих логическое преобразование исходных данных).
Расширенные мнемоники команд, ориентированных на использование вместе с логическими командами, также приведены в табл.
9.1. Отметим, что эти мнемоники зачастую являются аналогами уже изученных. Например, команды BE и BZ эквивалентны, так как и той и другой соответствует маска 10002. Заметим также, что маской, соответствующей мнемонике NOP (NO OPERATION — Нет операции), является 00002, и это означает, что переход по такой команде никогда не выполняется. Но для чего же может использоваться не выполняющая никаких действий команда? NOP используется в целях резервирования памяти для команды, которая, возможно, будет записана на ее место уже во время выполнения программы. Еще одним возможным применением NOP является ее указание вместе с командой ЕХ, которая будет описана .в гл. 13.
Существует 16 различных масок, не каждой из которых соответствует расширенная мнемоника. Если мы хотим передать управление по адресу LOCO при получении нулевого результата или при возникновении переполнения, мы должны написать
ВС 9, LOCO
Несмотря на то, что индексирование не часто используется в командах перехода, оно иногда бывает очень удобно для модификации адреса перехода при выполнении программы. В качестве примера подобного использования можно привести работу с таблицей переключателей.
ВС |
15,ВТАВ(9) |
ВС |
15.SEC1 |
ВС |
15.SEC2 |
ВС |
15.SEC3 |
ВС |
15.SEC4 |
Предположим, что в нашу программу включены четыре программные секции, первые команды которых имеют соответственно метки SEC1, SEC2, SEC3 и SEC4. Мы хотим приступить к выполнению какой-либо одной из этих секций в зависимости от результатов обработки некоторой информации. Организовать выполнение подобных действий можно так:
ВТАВ
Здесь мы использовали регистр 9 в качестве индексного. Предполагается, что содержимое регистра 9 установлено предыдущей частью программы. Если нужно перейти к выполнению SEC3, в регистр 9 должно быть предварительно занесено число 8. Читатели, знакомые с языком ФОРТРАН, могут считать, что данная последовательность команд эквивалентна вычисляемому GO ТО.
Другой пример использования таблицы переключателей связан с работой операционной системы. Многие программы, такие, например, как ассемблеры, компиляторы, редакторы связей и другие системные программы вырабатывают код завершения для индикации своего успешного или неуспешного выполнения. Этот код обычно кратен числу 4 и используется операционной системой аналогично значению индекса в предыдущем примере. Например, код 0 означает, что выполнение программы прошло успешно и нужно перейти к выполнению следующего шага задания. Код 4 может означать, что была отмечена ошибка, но ошибка несущественная для правильного выполнения следующего шага. Операционная система выдает предупреждающее сообщение и приступает к выполнению следующего шага. Код 8 обычно означает, что ошибка была столь серьезна, что дальнейшая работа с данным заданием нецелесообразна. Выполнение задания прекращается. Какие действия предпримет операционная система, определяется кодом завершения, который используется как индекс в таблице переключателей.
Команда BCR
Команда BCR является RR-модификацией команды ВС. Она работает абсолютно так же, как и команда ВС, только адрес, по которому следует совершить переход, выбирается из регистра R2, а не задается как операнд в памяти.
BCR M1,R2 |
Branch of Condition to Register |
PC(R2), если признак результата соответствует Ml |
Для использования команды BCR необходимо сначала загрузить адрес перехода в регистр. Маска определяется так же, как и для команды ВС. Вместо указания адреса, по которому следует передать управление, задается регистр R2, содержащий данный адрес. Предположим, что мы хотим осуществить переход по адресу SITU, если признак результата не равен нулю, используя команду BCR. Где-либо в программе до команды BCR нужно поместить команду
LA 5,SITU
которая загрузит адрес команды с меткой SITU в регистр 5. Затем следует написать команду
BCR 7,5
или
BCR В’0111′,5
В результате выполнения такой команды будет произведен переход по адресу, содержащемуся в регистре 5, если признак результата не равен нулю.
На рис. 9.1 изображена блок-схема программы, отыскивающей полуслово с минимальным числовым значением содержимого в последовательности из 15 полуслов, первое из которых имеет имя DATA.
На рис. 9.2 представлена соответствующая программа. Первые две команды загружают в регистры 9 и 10 адреса LESS и CHECKER. Регистр 3 — индексный, в качестве его начального содержимого устанавливается число 2. Конечное значение индекса — 28; это число загружается в регистр 5 и сравнивается в цикле с содержимым регистра 3. Команда BCR в одном случае проверяет, является ли рассматриваемое в настоящий момент число меньшим, чем минимальное из предыдущих (X на блок-схеме), а в другом случае используется для проверки условия выхода из цикла и завершения решения.
Рис. 9.1. Блок-схема программы нахождения и печати наименьшего из 15 чисел, записанных в последовательных полусловах, начиная с адреса DATA.
Рис. 9.2. Программа нахождения наименьшего из 15 чисел, записанных в последовательных полусловах, начиная с адреса DATA. Для реализации переходов используется команда BCR.
Машинная форма команды BCR имеет формат обычной RR-команды, лишь вместо R1 указывается 4-разрядная маска:
Например, команда
BCR 13,10
транслируется в
07DA
а команда
BCR 15,8 в 07F8
Существуют две расширенные мнемоники команды BCR. Мнемоника BR (Branch-to-Register) соответствует маске, обеспечивающей безусловный переход по адресу, указанному в регистре. Команда BR имеет следующий вид:
BR R2
Ей эквивалентна команда
BCR 15,R2
BR 14
В результате выполнения команды управление будет передано по адресу, указанному в регистре 14, независимо от значения признака результата. В процессе трансляции эта команда будет заменена ассемблером на
BCR B’1 111′,14
Другая расширенная мнемоника, NOPR, соответствует маске 0.
BCR 0,R2
Мнемоника NOPR используется для резервирования 2-байтового поля внутри программы, в Koiopoe впоследствии может быть внесена любая другая команда такой же длины. Первый байт зарезервированного таким образом поля содержит 07 (код операции BCR), а второй — 0R2. NOPR также может быть использована вместе с командой EX.
В некоторых моделях IBM 360/370 время выборки команд из памяти зависит от длины команды. При выполнении заданий на этих машинах выгодно, там где это возможно, использовать команды типа RR вместо RX, так как это сокращает время выполнения программы. Однако следует учитывать, что обычно выполнение команд типа RR требует белее длительной подготовки. Из рис. 9.2 видно, что если команду ВС мы хотим заменить командой BCR, то предварительно следует загрузить адрес перехода в соответствующий регистр. Время, тратящееся на выполнение команды LA, может превышать сэкономленное заменой время. Кроме того, для выполнения BCR в отличие от ВС требуется регистр, который, вообще говоря, может быть использован и для других целей. Таким образом, вопрос о том, какую из команд, ВС или BCR, использовать, не может быть решен однозначно — решение зависит от типа машины и от того, сколько раз выполняется команда перехода и сколько раз необходимо проводить предварительную подготовку.
Команда BCR обычно используется в том случае, когда имя команды, на которую следует передать управление, неизвестно. Эта команда может быть даже командой другой программы, что и имеет место в случае возврата из подпрограммы. Как мы увидим в гл. 13, одна из функций подпрограммы — организовать возврат в соответствующее место основной программы. Адрес возврата обычно передается подпрограмме через регистр 14. Таким образом, обычно последней в подпрограмме выполняется команда
BR 14
Другим примером использования BCR может служить организация работы с таблицей переключателей. Адрес, по которому следует произвести переход, вычисляется в предшествующей части программы и заносится в регистр. При необходимости произвести переход по вычисленному адресу используется команда BCR.
Команды перехода и вызова
В последней группе табл. В.2. представлены команды условного и безусловного переходов, вызова подпрограмм и возврата. Простейшей из них является команда JMP.
Метка в ней может указывать на целевой адрес или на содержимое любого действительного адреса. Следует учитывать различие между ближним и дальним переходами. Объектом ближнего перехода является текущий кодовый сегмент, неизменный в период выполнения операции. В процессе дальнего перехода значение регистра CS, напротив, меняется. В случае непосредственной адресации с применением метки новое значение регистра кодового сегмента устанавливается при вызове после метки; в случае с действительным адресом из памяти выбирается длинное слово, в результате младшее слово оказывается соответствующим целевой метке, а старшее слово — новому значению регистра кодового сегмента.
Ничего удивительного в таком различии нет. Чтобы выполнить переход к произвольному адресу в 20-разрядном адресном пространстве, необходим механизм определения адреса длиной более 16 бит. Этот механизм существует, и заключается он в присвоении регистрам CS и PC новых значений.
Условные переходы
В процессоре 8088 предусмотрены 15 разновидностей условных переходов, причем у некоторых из них сразу несколько имен (например, команды JUMP GREATER OR EQUAL и JUMP NOT LESS THAN эквивалентны). Все они перечислены в табл. В.З. Максимальное расстояние перехода составляет 128 байт от текущей команды. Если объект перехода находится вне допустимого диапазона, приходится реали-зовывать составной переход (JumP over JumP)- В этом случае для перехода через следующую команду применяется второй переход с противоположным условием. Если в следующей команде определен безусловный переход по целевому адресу, то сочетание этих двух команд приводит лишь к удлинению перехода указанного типа. К примеру:
JВ FARLABEL
Эту команду следует заменить следующей конструкцией:
JNA If
JMP FARLABEL
1:
Иными словами, если выполнение команды JUMP BELOW невозможно, составляется конструкция, состоящая из команды JUMP NOT ABOVE с близлежащей меткой 1 в качестве объекта и безусловного перехода к FARLABEL. Результаты в обоих случаях одинаковы, различие заключается лишь в больших или меньших временных и пространственных затратах. Если объект перехода находится слишком далеко, ассемблер автоматически организует составной переход. Правильно провести подобные вычисления довольно сложно. Предположим, что расстояние до объекта близко к максимально допустимому, и при этом некоторые промежуточные команды содержат условные переходы. Внешний переход может быть проведен только после определения расстояний внутренних переходов. В такой ситуации ассемблер предпринимает определенные меры предосторожности. К примеру, он может сформировать составной переход без строгой на то необходимости. Непосредственный условный переход формируется ассемблером только в том случае, если объект перехода совершенно точно находится на досягаемом расстоянии.
Таблица В.З- Условные переходы | ||
Команда | Описание | Условие перехода |
JNA, JBE | Ниже или равно | CF = 1 или ZF = 1 |
JNB, JAE, JNC | Не ниже | CF = 0 |
JE, JZ | Нуль, равно | ZF= 1 |
t@tabl_body -= JNLE, JG | Больше чем | SF = OF и ZF = 0 |
JGE, JNL | Больше или равно | SF = OF |
JO | Переполнение | OF= 1 |
JS | Отрицательный знак | SF = 1 |
JCXZ | Значение СХ равно нулю | cx = o |
JB, JNAE, JC | Ниже | CF= 1 |
JNBE, JA | Выше | CF = 0 и ZF = 0 |
JNE, JNZ | Не равно нулю, не равно | ZF = 0 |
JL, JNGE | Меньше чем | SF*OF |
JLE, JNG | Меньше или равно | SF ф OF или ZF = 1 |
JNO | Без переполнения | OF = 0 |
JNS | Неотрицательно | SF = 0 |
В большинстве своем условные переходы зависят от флагов состояния и предваряются командами сравнения или проверки. Команда СМР вычитает исходный операнд из целевого операнда, устанавливает коды состояния, после чего сбрасывает результат. Ни один из операндов при этом не меняется. При нулевом результате или установленном знаковом бите (обозначающем отрицательный результат) устанавливается соответствующий флаговый бит. Если результат не представляется возможным выразить допустимым числом битов, устанавливается флаг переполнения. Если старший бит сопровождается переносом, устанавливается флаг переноса. При условных переходах все эти биты можно проверить.
Для обработки операндов со знаками используются команды GREATER THAN и LESS THAN. Операнды без знаков обрабатываются командами ABOVE и BELOW.
⇐Операции организации циклов и повторяющиеся строковые операции || Оглавление || Вызовы подпрограмм⇒
7.6.2. Команды условного перехода
Все команды условного перехода выполняются по схеме
if <условие перехода> then goto L
и производят близкий короткий относительный переход, если выполнено некоторое условие перехода, в противном случае продолжается последовательное выполнение команд программы. На Паскале условие перехода чаще всего задают в виде условного оператора
if op1 <отношение> op2 then goto L
где отношение – один из знаков операции отношения = (равно), <> (не равно), > (больше), < (меньше), <= (меньше или равно), >= (больше или равно). Если обозначить rez=op1–op2, то оператор условного перехода можно записать в эквивалентном виде сравнения с нулём
if rez <отношение> 0 then goto L
Все машинные команды условного перехода, кроме одной, вычисляют условие перехода, анализируя один, два или три флага из регистра флагов, и лишь одна команда условного перехода вычисляет условие перехода, анализируя значение регистра CX. Команда условного перехода в языке Ассемблер имеет вид
j<мнемоника перехода> i8; IP := (IP + i8)mod 216
Мнемоника перехода (это от одной до трёх букв) связана со значением анализируемых флагов (или регистра CX), либо со способом формирования этих флагов. Чаще всего программисты формируют флаги, проверяя отношение между двумя операндами op1 <отношение> op2, для чего выполняется команда вычитания или команда сравнения. Команда сравнения имеет мнемонический код операции cmp и такой же формат, как и команда вычитания:
cmp op1,op2
Она и выполняется точно так же, как команда вычитания за исключением того, что разность не записывается на место первого операнда. Таким образом, единственным результатом команды сравнения является формирование флагов, которые устанавливаются так же, как и при выполнении команды вычитания. Вспомним, что программист может трактовать результат вычитания (сравнения) как производимый над знаковыми или же беззнаковыми числами. Как мы уже знаем, от этой трактовки зависит и то, будет ли один операнд больше другого или же нет. Так, например, рассмотрим два коротких целых числа 0FFh и 01h. Как знаковые числа 0FFh = -1 < 01h = 1, а как беззнаковые числа 0FFh = 255 > 01h = 1.
Исходя из этого, принята следующая терминология: при сравнении знаковых целых чисел первый операнд может быть больше (greater) или меньше (less) второго операнда. При сравнении же беззнаковых чисел будем говорить, что первый операнд выше (above) или ниже (below) второго. Ясно, что действию «выполнить переход, если первый операнд больше второго» будут соответствовать разные машинные команды, если трактовать операнды как знаковые или же беззнаковые целые числа. Это учитывается в различных мнемониках этих команд.
Ниже в Таблице 7.1 приведены мнемоники команд условного перехода. Некоторые команды имеют разную мнемонику, но выполняются одинаково (переводятся программой Ассемблера в одну и ту же машинную команду), такие команды указаны в одной строке таблицы.
Таблица 7.1. Мнемоника команд условного перехода | ||
КОП | Условие перехода | |
Логическое условие перехода | Результат (rez) команды вычитания или cоотношение операндов op1 и op2 команды сравнения | |
je jz | ZF = 1 | Rez = 0 или op1 = op2 (результат = 0, операнды равны) |
jne jnz | ZF = 0 | rez <> 0 или op1 <> op2 Результат <> 0, операнды не равны |
jg jnle | (SF=OF) and (ZF=0) | rez > 0 или op1 > op2 Знаковый результат > 0, op1 больше op2 |
jge jnl | SF = OF | rez >= 0 или op1 >= op2 Знаковый результат >= 0, т.е. op1 больше или равен (не меньше) op2 |
jl jnge | SF <> OF | rez < 0 или op1 < op2 Знаковый результат < 0, т.е. op1 меньше (не больше или равен) op2 |
jle jng | (SF<>OF) or (ZF=1) | rez <= 0 или op1 <= op2 Знаковый результат <= 0, т.е. op1 меньше или равен(не больше) op2 |
ja jnbe | (CF=0) and (ZF=0) | rez > 0 или op1 > op2 Беззнаковый результат > 0, т.е. op1 выше (не ниже или равен) op2 |
jae jnb jnc | CF = 0 | rez >= 0 или op1 >= op2 Беззнаковый результат >= 0, т.е. op1 выше или равен (не ниже) op2 |
jb jnae jc | CF = 1 | rez < 0 или op1 < op2 Беззнаковый результат < 0, т.е. op1 ниже (не выше или равен) op2 |
jbe jna | (CF=1) or (ZF=1) | rez >= 0 или op1 >= op2 Беззнаковый результат >= 0, т.е. op1 ниже или равен (не выше) op2 |
js | SF = 1 | Знаковый бит разультата (7-й или 15-ый, в зависимости от размера) равен единице |
jns | SF = 0 | Знаковый бит разультата (7-й или 15-ый, в зависимости от размера) равен нулю |
jo | OF = 1 | Флаг переполнения равен единице |
jno | OF = 0 | Флаг переполнения равен нулю |
jp jpe | PF = 1 | Флаг чётности 1 равен единице |
jnp jpo | PF = 0 | Флаг чётности равен единице |
jcxz | CX = 0 | Значение регистра CX равно нулю |
В качестве примера рассмотрим, почему условному переходу jl/jnge соответствует логическое условие перехода SF<>OF. При выполнении команды сравнения cmp op1,op2 или команды вычитания sub op1,op2 нас будет интересовать трактовка операндов как знаковых целых чисел, поэтому возможны два случая, когда первый операнд меньше второго. Во-первых, если при выполнении операции вычитания op1-op2 результат получился правильным, т.е. не было переполнения (OF=0), то бит знака у правильного результата равен единице (SF=1). Во-вторых, при вычитании мог получиться неправильный результат, т.е. было переполнение (OF=0), но в этом случае знаковый бит результата будет неправильным, т.е. равным нулю. Видно, что в обоих случаях эти два флага не равны друг другу, т.е. должно выполняться условие SF<>OF, что и указано в нашей таблице. Для тренировки разберите правила формирования и других условий переходов.
Как видим, команд условного перехода достаточно много, поэтому понятно, почему для них реализован только один формат – близкий короткий относительный переход. Реализация других форматов команд условного перехода привела бы к резкому увеличению числа команд в языке машины и, как следствие, к усложнению центрального процессора и росту объёма программ. В то же время наличие только одного формата команд условного перехода может приводить к плохому стилю программирования. Пусть, например, надо реализовать на Ассемблере условный оператор языка Паскаль
if X>Y then goto L;
Соответствующий фрагмент на языке Ассемблера, реализующий этот оператор для знаковых X,Y
mov ax,X
cmp ax,Y
jg L
. . .
L:
может быть неверным, если расстояние между меткой и командой условного перехода велико (не помещается в байт). В таком случае придётся использовать такой фрагмент на Ассемблере со вспомогательной меткой L1 и вспомогательной командой безусловного перехода:
mov ax,X
cmp ax,Y
jleL1
jmp L
L1:
. . .
L:
Таким образом, на самом деле мы вынуждены реализовывать такой фрагмент программы на языке Паскаль:
if X<=Y then goto L1; goto L; L1:;
Это, конечно, по необходимости, прививает плохой стиль программирования.
В качестве примера использования команд условного перехода рассмотрим программу, которая вводит знаковое число A в формате слова и вычисляет значение X по формуле
include io.asm
; файл с макроопределениями для макрокоманд ввода-вывода
data segment
A dw ?
X dw ?
Diagn db ′Ошибка – большое значение!$′
Data ends
Stack segment stack
db 128 dup (?)
stack ends
code segment
assume cs:code, ds:data, ss:stack
start:mov ax,data; это команда формата r16,i16
mov ds,ax ; загрузка сегментного регистра DS
inint A ; ввод целого числа
mov ax,A ; ax := A
mov bx,ax ; bx := A
inc ax ; ax := A+1
jo Error
cmp bx,2 ; Сравнение A и 2
jle L1 ; Вниз по первой ветви вычисления X
dec bx ; bx := A-1
jo Error
imul bx ; (dx,ax):=(A+1)*(A-1)
jo Error ; Произведение (A+1)*(A-1) не помещается в ax
L: mov X,ax ; Результат берётся только из ax
outint X; Вывод результата
newline
finish
L1: jl L2; Вниз по второй ветви вычисления X
mov ax,4
jmp L; На вывод результата
L2: mov bx,7; Третья ветвь вычисления X
cwd ; (dx,ax):= длинное (A+1) – иначе нельзя!
idiv bx; dx:=(A+1) mod 7, ax:=(A+1) div 7
mov ax,dx
jmp L; На вывод результата
Error:mov dx,offset Diagn
outstr
newline
finish
code ends
end start
В нашей программе мы сначала закодировали вычисление по первой ветви нашего алгоритма, затем по второй и, наконец, по третьей. Программист, однако, может выбрать и другую последовательность кодирования ветвей, это не влияет на суть дела. В нашей программе предусмотрена выдача аварийной диагностики, если результаты операций сложения (A+1), вычитания (A-1) или произведения (A+1)*(A-1) слишком велики и не помещается в одно слово.
Для увеличения и уменьшения операнда на единицу мы использовали команды
inc op1 и dec op1
Здесь op1 может иметь формат r8, r16, m8 и m16. Например, команда inc ax эквивалентна команде add ax,1 , но не меняет флага CF. Таким образом, после этих команд нельзя проверить флаг переполнения, чтобы определить, правильно ли выполнились такие операции над беззнаковыми числами.
Обратите также внимание, что мы использовали команду длинного деления, попытка использовать здесь короткое деление, например
L2: mov bh,7; Третья ветвь вычисления X
idivbh; ah:=(A+1)mod7, al:=(A+1)div7
может привести к ошибке. Здесь остаток от деления (A+1) на число 7 всегда поместится в регистр ah, однако частное (A+1) div 7 может не поместиться в регистр al (пусть A=27999, тогда (A+1) div 7 = 4000 – не поместится в регистр al).
При использовании команд условного перехода мы предполагали, что расстояние от точки перехода да нужной метки небольшое (формата i8), если это не так, то программа Ассемблера выдаст нам соответствующую диагностику об ошибке и нам придётся использовать «плохой стиль программирования», как объяснялось выше. В нашей программе это может случиться только тогда, когда суммарный размер кода, подставляемого вместо макрокоманд outint и finish, будет больше 128 байт (обязательно понять это!).
Лекция по языку ассемблера и компьютерной архитектуре (CS 301)
Лекция по языку ассемблера и компьютерной архитектуре (CS 301) CS 301: Лекция по программированию на ассемблере, доктор ЛоулорПрыжок инструкция, такая как «jmp», просто переключает ЦП на выполнение другой фрагмент кода. Это ассемблерный эквивалент «goto», но, в отличие от goto, прыжки не считаются постыдными в сборка. (Дейкстра написал в 1968 году статью под названием «Goto Считается вредным «.С тех пор goto обычно считались вредными, за исключением Linux.)
Вы говорите, куда перейти, используя «ярлык перехода», который может быть любым
имя строки с двоеточием после него. (Такой же точный синтаксис
используется в C / C ++)
В обоих случаях мы возвращаем 3, потому что мы перепрыгиваем через 999
назначение. Прыжки в некоторой степени полезны для перепрыгивания плохих
код, но он действительно становится полезным, когда вы добавляете условный
прыгает.Они используются после инструкции «cmp» для сравнения
два значения.
Инструкция | Полезно для … |
jmp | Всегда прыгать |
je | Перейти, если cmp равно |
jne | Перейти, если cmp не равно |
jg | Подписано>
(больше) |
jge | Подпись> = |
jl | Подпись <
(менее) |
ил | Подпись <= |
и | Без знака> (вверху) |
иже | Без знака> = |
сп | Без знака <(внизу) |
jbe | Без знака <= |
jecxz | Перейти, если ecx 0 (Серьезно !?) |
jc | Перейти при переносе: используется для неподписанных
переполнение, или multiprecision добавить |
ио | Перейти, если там был подписан перелив |
Есть также «n» версий НЕ для каждого прыжка; например «jno» прыгает, если есть НЕ переполнение.
Условный Прыжки: разветвление в сборке
В сборе, все ветвления выполняются с использованием двух типов инструкций:
- Команда сравнения, такая как «cmp», сравнивает два значения.
Внутри он делает это путем их вычитания.
- Команда условного перехода, например «je» (переход, если равен),
делает переход где-нибудь, если два значения удовлетворяют
правильное состояние. Например, если значения равны,
их вычитание дает ноль, поэтому «je» то же самое, что «jz».
Вот как используйте сравнение и прыжок, если равно («je»):
mov eax, 3
cmp eax, 3; как eax по сравнению с 3?
je lemme_outta_here; если равно, то перескочить на
mov eax, 999; <- не выполняется * если * перепрыгиваем через него
lemme_outta_here:
ret
(Попробуйте это сейчас в NetRun!)
Вот сравнить и прыжок-если-меньше-чем («jl»):
mov eax, 1
cmp eax, 3; как eax по сравнению с 3?
jl lemme_outta_here; если меньше, то перескочить на
mov eax, 999; <- не выполняется * если * перепрыгиваем
lemme_outta_here:
ret
(Попробуйте это сейчас в NetRun!)
C ++ эквивалентно сравнить-и-прыгать-если-что бы то ни было «если (что-то) перейти где-то;».
Петли
Чтобы зациклиться, вы просто вернитесь к началу кода. Где-то ты делаешь нужно условное выражение, или вы создали бесконечный цикл!
В этом Например, мы отсчитываем edi, пока он не достигнет нуля.
; edi - наш первый аргумент функции
mov eax, 0; сумма добавлена сюда Начало: ; цикл начинается здесь добавить eax, 10; добавлять каждый раз по петле sub edi, 1; приращение цикла cmp edi, 0; петлевой тест jg start; продолжить цикл, если edi> 0 ret
(Попробуйте это сейчас в NetRun!)
Это построчно эквивалентно этому коду C ++:
int foo (int bar) { int sum = 0; Начало: сумма + = 10; бар--; если (bar> 0) перейти к началу; сумма возврата; }
(Попробуйте сейчас в NetRun!)
Оф конечно, это очень уродливый код на C ++! Это более идиоматично напишите здесь цикл «for»:
int foo (int bar) { int sum = 0; for (int count = bar; count> 0; count--) сумма + = 10; сумма возврата; }
(Попробуйте это сейчас в NetRun!)
Подробнее Сложный поток управления: C —
Можно на самом деле написать очень своеобразный вариант C ++, где «если» операторы содержат только операторы goto.Мое шутливое название для этот C ++ в стиле сборки — «C—»: вы используете только «+ =» и «* =» арифметика и «если (простой тест) перейти куда-нибудь»; управление потоком.
Например, это совершенно допустимый C ++ в стиле «C—«:
int main () {
int я = 0;
if (i> = 10) goto byebye; до свидания;
std :: cout << "Не слишком большой! \ N";
до свидания: возврат 0;
}
Этот способ написание C ++ очень похоже на сборку — фактически, есть взаимно однозначное соответствие между строками кода C, написанными таким образом и инструкции на машинном языке.Более сложный C ++, подобно конструкции «for», расширяется до многих линий сборки.
int i, n = 10;
for (i = 0; istd :: cout << "В цикле: i ==" << i << "\ n";
}
Вот один расширенная версия этого C / C ++ цикла «for»:
int i = 0, n = 10;
start: std :: cout << "В цикле: i ==" << i << "\ n";
i ++;
, если (i(исполняемый Ссылка NetRun)
Вы должны убедить себя, что это действительно эквивалентно цикл «for» во всех случаях.Осторожно - если n - параметр, это не! (Что, если n> = i?)
Все конструкции управления потоком C могут быть написаны с использованием только «if» и "goto", которые обычно сопоставляют "один к одному" для сравнения и перехода. последовательность в сборке.
Нормальный C Расширенный C если (A) {
...
}if (! A) goto END;
{
...
}
КОНЕЦ:если (! A) {
...
}if (A) goto END;
{
...
}
КОНЕЦ:если (A && B) {
...
}if (! A) goto END;
if (! B) goto END;
{
...
}
КОНЕЦ:если (A || B) {
...
}if (A) goto STUFF;
, если (B) перейти к STUFF;
goto END;
ПЕРСОНАЛ:
{
...
}
КОНЕЦ:, а (А) {
...
}goto TEST;
START:
{
...
}
TEST: если (A) перейти к START;do {
...
} в то время как (A)START:
{
...
}
if (A) перейти к START;для (i = 0; i {
...
}я = 0; / * Версия A * /
goto TEST;
START:
{
...
}
i ++;
ТЕСТ: if (iдля (i = 0; i {
...
}я = 0; / * Версия B * /
START: if (i> = n) goto END;
{
...
}
i ++;
goto START;
КОНЕЦ:
Обратите внимание, что последние два перевода понятия «для» (помечены Версия A и версия B) вычисляют одно и то же. Который один быстрее? Если цикл повторяется много раз, я требую версию (A) обычно быстрее, так как есть только один (условный) goto каждый раз по циклу вместо двух goto в версии (Б) - одно условное и одно безусловное.Но версия (B) вероятно, быстрее, если n часто равно 0, потому что в этом случае быстро переходит в END (за один условный переход).ВНИМАНИЕ: Философское содержание
Ядерная оружие настолько мощное, что обычно считается плохой идеей используйте их для решения большинства задач.
Аналогично, Операторы goto настолько мощны, что обычно считаются плохими идея использовать их для большинства задач. Проблема в том, когда ты увидеть goto, вы не можете сказать, используется ли он для имитации for, while, вызов функции или какой-то более странный их гибрид.Таким образом goto может быть достаточно легко написать, но его труднее читать, и поскольку код в больших проектах пишется только один раз, но получает прочитал много раз много разных людей за эти годы, это больше важно сделать ваш код легко читаемым.
Я больше всего замечаю программисты-самоучки (включая меня), как правило, предпочитают goto или его немного более классный кузен, цикл while, потому что они более Генеральная. Но после того, как вы испортили цикл "while" достаточно раз, например, отказавшись от приращения цикла и непреднамеренно сделав бесконечный цикл, вы в конечном итоге по умолчанию используете циклы "for", потому что синтаксис, предписанный компилятором ("for (int i = 0; i
Intel x86 JUMP краткий справочник
Восприятие прыжков и флажков долгое время было проблемой. область для меня, тем более что книга ассемблера Intel показывает 32 из все с похожими именами. Присмотревшись, я обнаружил, что многие инструкции являются синонимами друг друга, и на практике вся гамма не нужна, а в процессе обнаружил, что моя копия Справочника программиста Intel 80386 В руководстве некорректно описана одна из инструкций.
Итак, я сгруппировал их функционально, со всеми инструкциями синонимы в одном ряду.
Инструкция Описание подпись Флаги короткий
переход
коды операцийоколо
скачок
опкодовJO Перейти при переполнении OF = 1 70 0F 80 JNO Перейти, если не переполнение OF = 0 71 0F 81 СП Перейти, если знак SF = 1 78 0F 88 JNS Перейти, если не подписать SF = 0 79 0F 89 JE
JZПерейти, если равно
Перейти, если нольZF = 1 74 0F 84 JNE
JNZПерейти, если не равно
Перейти, если не нольZF = 0 75 0F 85 JB
JNAE
JCПерейти, если ниже
Перейти, если не больше или равно
Прыгать с переноскибез знака CF = 1 72 0F 82 JNB
JAE
JNCПерейти если не ниже
Перейти, если больше или равно
Перейти, если нетбез знака CF = 0 73 0F 83 JBE
JNAПерейти, если меньше или равно
Перейти, если не вышебез знака CF = 1 или ZF = 1 76 0F 86 JA
JNBEПерейти, если выше
Перейти, если не меньше или равнобез знака CF = 0 и ZF = 0 77 0F 87 JL
JNGEПерейти если меньше
Перейти, если не больше или равноподписано SF <> OF 7C 0F 8C JGE
JNLПерейти, если больше или равно
Перейти, если не менееподписано SF = OF 7D 0F 8D JLE
JNGПерейти, если меньше или равно
Перейти, если не большеподписано ZF = 1 или SF <> OF 7E 0F 8E JG
JNLEПерейти, если больше
Прыжок, если не меньше или равноподписано ZF = 0 и SF = OF 7F 0F 8F JP
JPEПерейти, если четность
Перейти, если четность дажеПФ = 1 7A 0F 8A JNP
JPOПерейти, если не четность
Перейти, если четность нечетнаяПФ = 0 7Б 0F 8B JCXZ
JECXZПерейти, если регистр% CX равен 0
Перейти, если регистр% ECX равен 0% CX = 0
% ECX = 0E3 Процессоры x86 имеют большой набор флагов, которые представляют состояние процессора, а инструкции условного перехода могут от них в сочетании.
- CF - флаг для переноски
- Установить на перенос или заимствование старшего разряда; растаможен в противном случае
- PF - флаг четности
- Устанавливается, если младшие восемь бит результата содержат четное число бит "1"; растаможен в противном случае
- ZF - нулевые флаги
- Устанавливается, если результат равен нулю; очищено в противном случае
- SF - флаг знак
- Установить равным старшему биту результата (0, если положительный, 1, если отрицательный)
- OF - флаг переполнения
- Устанавливается, если результат слишком большое положительное число или слишком мало отрицательное число (исключая знаковый бит), чтобы соответствовать операнду назначения; очищено иначе
Перейти, если условие выполнено
JB - Перейти, если условие выполнено
Код Мнемоника Описание 77 CB JA rel8 Короткий переход, если выше (CF = 0 и ZF = 0) 73 CB JAE rel8 Короткий переход, если он больше или равен (CF = 0) 72 куб JB rel8 Короткий переход, если ниже (CF = 1) 76 CB JBE rel8 Короткий переход, если ниже или равно (CF = 1 или ZF = 1) 72 куб JC rel8 Короткий прыжок при переноске (CF = 1) E3 CB JCXZ rel8 Короткий переход, если регистр CX 0 E3 CB JECXZ rel8 Короткий переход, если регистр ECX 0 74 куб. JE rel8 Короткий переход при равенстве (ZF = 1) 7F CB JG rel8 Короткий переход, если больше (ZF = 0 и SF = OF) 7D CB JGE rel8 Короткий переход, если больше или равно (SF = OF) 7C CB JL отн8 Короткий переход, если меньше (SF <> OF) 7E CB JLE rel8 Короткий переход, если меньше или равно (ZF = 1 или SF <> OF) 76 CB JNA rel8 Короткий переход, если не выше (CF = 1 или ZF = 1) 72 куб JNAE rel8 Короткий прыжок, если он не выше или равен (CF = 1) 73 CB JNB rel8 Короткий переход, если не ниже (CF = 0) 77 CB JNBE rel8 Короткий переход, если он не ниже или равен (CF = 0 и ZF = 0) 73 CB JNC rel8 Короткий переход, если не переносится (CF = 0) 75 куб. JNE rel8 Короткий переход, если не равен (ZF = 0) 7E CB JNG rel8 Короткий переход, если не больше (ZF = 1 или SF <> OF) 7C CB JNGE rel8 Короткий переход, если не больше или равно (SF <> OF) 7D CB JNL rel8 Короткий прыжок, если не меньше (SF = OF) 7F CB JNLE rel8 Короткий переход, если не меньше или равно (ZF = 0 и SF = OF) 71 куб JNO rel8 Короткое замыкание, если не переполнение (OF = 0) 7B CB JNP rel8 Короткий переход, если нет четности (PF = 0) 79 CB JNS rel8 Короткий переход, если нет знака (SF = 0) 75 куб. JNZ rel8 Короткий переход, если не ноль (ZF = 0) 70 куб. JO rel8 Короткое замыкание при переполнении (OF = 1) 7A CB JP rel8 Короткое замыкание при четности (PF = 1) 7A CB JPE rel8 Короткий переход при четности (PF = 1) 7B CB JPO rel8 Короткий переход, если четность нечетная (PF = 0) 78 куб JS rel8 Короткий переход, если знак (SF = 1) 74 CB JZ rel8 Короткое замыкание, если ноль (ZF = 1) 0F 87 ч / кд JA rel16 / 32 Перейти рядом, если выше (CF = 0 и ZF = 0) 0F 83 ч / кд JAE rel16 / 32 Перейти рядом, если больше или равно (CF = 0) 0F 82 cw / cd JB rel16 / 32 Перейти рядом, если ниже (CF = 1) 0F 86 ч / кд JBE rel16 / 32 Перейти рядом, если ниже или равно (CF = 1 или ZF = 1) 0F 82 cw / cd JC rel16 / 32 Прыгнуть, если несут (CF = 1) 0F 84 ч / кд JE rel16 / 32 Перейти почти, если равно (ZF = 1) 0F 84 ч / кд JZ rel16 / 32 Перейти рядом, если 0 (ZF = 1) 0F 8F cw / cd JG отн.16 / 32 Перейти рядом, если больше (ZF = 0 и SF = OF) 0F 8D cw / cd JGE rel16 / 32 Перейти рядом, если больше или равно (SF = OF) 0F 8C против часовой / CD JL отн.16 / 32 Перейти рядом, если меньше (SF <> OF) 0F 8E cw / cd JLE rel16 / 32 Перейти рядом, если меньше или равно (ZF = 1 или SF <> OF) 0F 86 ч / кд JNA rel16 / 32 Перейти рядом, если не выше (CF = 1 или ZF = 1) 0F 82 cw / cd JNAE rel16 / 32 Перейти рядом, если не больше или равно (CF = 1) 0F 83 ч / кд JNB rel16 / 32 Перейти рядом, если не ниже (CF = 0) 0F 87 ч / кд JNBE rel16 / 32 Перейти рядом, если не ниже или равно (CF = 0 и ZF = 0) 0F 83 ч / кд JNC rel16 / 32 Прыгать, если не переносить (CF = 0) 0F 85 ч / кд JNE rel16 / 32 Перейти рядом, если не равно (ZF = 0) 0F 8E cw / cd JNG rel16 / 32 Перейти рядом, если не больше (ZF = 1 или SF <> OF) 0F 8C против часовой / CD JNGE rel16 / 32 Перейти рядом, если не больше или равно (SF <> OF) 0F 8D cw / cd JNL отн.16 / 32 Перейти рядом, если не меньше (SF = OF) 0F 8F cw / cd JNLE rel16 / 32 Перейти рядом, если не меньше или равно (ZF = 0 и SF = OF) 0F 81 cw / cd JNO rel16 / 32 Перейти рядом, если не переполнение (OF = 0) 0F 8B cw / cd JNP, отн.16 / 32 Перейти близко, если нет четности (PF = 0) 0F 89 cw / cd JNS rel16 / 32 Перейти рядом, если нет знака (SF = 0) 0F 85 ч / кд JNZ rel16 / 32 Перейти к нулю, если не к нулю (ZF = 0) 0F 80 ч / кд JO rel16 / 32 Перейти при переполнении (OF = 1) 0F 8A cw / cd JP отн.16 / 32 Перейти к ближнему, если четность (PF = 1) 0F 8A cw / cd JPE отн16 / 32 Перейти, если четность четна (PF = 1) 0F 8B cw / cd JPO rel16 / 32 Перейти рядом, если четность нечетная (PF = 0) 0F 88 ч / кд JS отн.16 / 32 Перейти к значку if (SF = 1) 0F 84 ч / кд JZ rel16 / 32 Перейти рядом, если 0 (ZF = 1)
Описание
Проверяет состояние одного или нескольких флагов состояния в регистре EFLAGS (CF, OF, PF, SF и ZF) и, если флаги находятся в указанном состоянии (условии), выполняет переход к целевой инструкции, указанной в операнд назначения.Код условия (cc) связан с каждой инструкцией, чтобы указать условие, которое проверяется. Если условие не выполняется, переход не выполняется, и выполнение продолжается с инструкции, следующей за инструкцией Jcc.Целевая инструкция указывается с относительным смещением (смещение со знаком относительно текущего значения указателя инструкции в регистре EIP). Относительное смещение (rel8, rel16 или rel32) обычно указывается как метка в ассемблерном коде, но на уровне машинного кода оно кодируется как подписанное, 8-битное или 32-битное непосредственное значение, которое добавляется к указатель инструкции.Кодирование инструкций наиболее эффективно для смещений от 128 до +127. Если атрибут размера операнда равен 16, два старших байта регистра EIP очищаются до 0, в результате чего максимальный размер указателя инструкции составляет 16 бит.
Условия для каждой мнемоники Jcc приведены в столбце «Описание» таблицы на предыдущей странице. Термины «меньше» и «больше» используются для сравнения целых чисел со знаком, а термины «выше» и «ниже» используются для целых чисел без знака.
Поскольку конкретное состояние флагов состояния иногда можно интерпретировать двояко, для некоторых кодов операций определены две мнемоники.Например, инструкция JA (переход, если выше) и инструкция JNBE (переход, если не ниже или равно) являются альтернативными мнемониками для кода операции 77H.
Инструкция Jcc не поддерживает далекие переходы (переходы к другим сегментам кода). Когда цель для условного перехода находится в другом сегменте, используйте условие, противоположное проверяемому условию для инструкции Jcc, а затем получите доступ к цели с безусловным дальним переходом (инструкция JMP) к другому сегменту. Например, следующий условный дальний прыжок недопустим:
JZ FARLABEL;
Чтобы совершить этот дальний прыжок, используйте следующие две инструкции:
JNZ BEYOND;
JMP FARLABEL;
ЗА ПРЕДЕЛАМИ:
Инструкции JECXZ и JCXZ отличаются от других инструкций Jcc, поскольку они не проверяют флаги состояния.Вместо этого они проверяют содержимое регистров ECX и CX соответственно на 0. В соответствии с атрибутом размера адреса выбирается регистр CX или ECX. Эти инструкции полезны в начале условного цикла, который завершается инструкцией условного цикла (например, LOOPNE). Они предотвращают вход в цикл, когда регистр ECX или CX равен 0, что приведет к тому, что цикл будет выполняться 2 32 или 64 К раз, соответственно, вместо нуля раз.Все условные переходы преобразуются в выборки кода одной или двух строк кэша, независимо от адреса перехода или кэшируемости.
циклы применяются к прыжку и без прыжка
Операнды байтов Часы около 8 2 1 PV около 16 3 1 PV Флаги
ID незатронутые DF незатронутые VIP незатронутые IF незатронутые VIF незатронутые TF незатронутые переменного тока незатронутые SF незатронутые ВМ незатронутые ZF незатронутые РФ незатронутые AF незатронутые NT незатронутые ПФ незатронутые IOPL незатронутые CF незатронутые ИЗ незатронутые Инструкция Jump
Инструкция JumpОтвет:
Адрес Инструкция
(подробности опущены)ПК сразу после того, как эта инструкция
выполнила
(внизу цикла)............... ........... 00450000 00450000 нагрузка 00450004 00450004 нагрузка 00450008 00450008 добавить 0045000C 0045000C магазин 00450010 00450010 переход 0x00450000 004500 14 004500 00 В наши схематические программы, инструкция "перехода" загрузил ПК с 32-битным адресом.
Как 32-битная инструкция определяет 32-битный адрес? Некоторые биты инструкции должны использоваться для кода операции. Вот форма инструкции перехода на языке ассемблера.
j target # после задержки в один машинный цикл, # ПК <- адрес целиВот форма инструкции на машинном языке:
6 26 000010 00000000000000000000000000 - поля инструкции opcode target - значение полейТам есть место в инструкции для 26-битный адрес.26-битное поле целевого адреса преобразован в 32-битный адрес. Закончено во время выполнения, когда выполняется инструкция перехода.
Инструкции всегда начинаются с адреса, который является кратным из четырех (они выровнены по словам). Итак, младшие два бита 32-битного адреса инструкции всегда "00". Сдвиг 26-битной цели влево на два места приводит к 28-битной адрес с выравниванием по словам (два младших бита становятся "00".)
После сдвига нам нужно заполнить четыре старших бита адреса. Эти четыре бита происходят из четыре старших бита в ПК. Они связаны со старшим концом 28-битного адреса. чтобы сформировать 32-битный адрес.
Например, вот машинный язык для инструкция, которая переходит в ячейку
0x5B145188
. Скажите, что инструкция находится по адресу адрес0x56767250
.
ВОПРОС 5:
Пока это происходит, какой адрес находится на ПК?
x86 - Сборка - инструкция перехода в машинном коде
Короткий переход использует смещение со знаком, добавленное к адресу инструкции, следующей за
JMP
.Например, первый
JMP L2
имеет смещениеFE
, что равно-2
, и добавление его к адресу инструкции, следующей заJMP
, дает вам адрес этогоJMP
.Однако это , а не , случай для первого
JMP L
, поскольку необходимое смещение будетE8
(второйJMP L
также неверен, он должен иметь смещениеE6
). Вы можете подтвердить это, если зайдете на онлайн-сайт ассемблера x86, такой как этот, и введете:mov ecx, 2 l: mov edx, 0 inc edx sub ecx, 1 нет нет нет Setz Al шл ал, 1 mov byte [l1 + 1], al l1: jmp l jmp l mov byte [l2 + 1], al l2: jmp l jmp l2 mov eax, edx Ret
Вы заметите эти дополнительные три строки
NOP
, потому что ассемблер выбирает более короткий вариантSUB ECX, 1
, и я просто хочу, чтобы адреса совпадали с тем, что у вас есть.Собранный код из этого выглядит следующим образом:0: b9 02 00 00 00 mov ecx, 0x2 00000005
: 5: ba 00 00 00 00 mov edx, 0x0 а: 42 вкл edx b: 83 e9 01 sub ecx, 0x1 э: 90 ноп ж: 90 нп 10: 90 ноп 11: 0f 94 c0 sete al 14: d0 e0 shl al, 1 16: a2 1c 00 00 00 mov ds: 0x1c, al 0000001b : 1b: eb e8 jmp 5 1d: eb e6 jmp 5 1f: a2 25 00 00 00 mov ds: 0x25, al 00000024 : 24: eb fe jmp 24 26: eb fc jmp 24 28: 89 d0 mov eax, edx 2a: c3 ret Из этого видно, что кодировка первых двух переходов неверна в вашем опубликованном коде.Это должно быть
EbE8 / EbE6
, а неEBBD / EBEB
. Фактически, последняя пара даже не имела бы смысла, если бы они перешли на какой-то другой лейбл , поскольку разница между ними должна быть ровно две, если они перейдут на тот же лейбл .Однако следует опасаться одной вещи: если вы внимательно изучите код, вы увидите, что он на самом деле самомодифицируется, поскольку инструкции
JMP
изменяются такими операторами, как:БАЙТ ДВИЖЕНИЯ [L1 + 1], AL
(изменение смещения инструкции на
L1
).Самомодифицирующийся код может использоваться для целей обфускации или затруднения обратного проектирования программного обеспечения, и может быть, что код уже претерпел изменения, которые будут применены.Было бы полезно наблюдать за этим кодом динамически по мере того, как производятся самоизменения , , чтобы увидеть, как они влияют на код, но приблизительный результат моего статического анализа выглядит следующим образом:
Адресный эффект ------- ------ 00 ecx = 2 05 edx = 0 0a edx = 1 0b ecx = 1, zflag = F 11 al = 0 (поскольку zflag = F) 14 лет 0 16 инструкция в 1b становится eb00, jmp 1d 1b переходит на 1d 1d переходит на 0a 0a edx = 2 0b ecx = 0, zflag = T 11 al = 1 (поскольку zflag = T) 14 al = 2 16 инструкция в 1b становится eb02, jmp 1f 1b переходит в 1f Инструкция 1f в 24 становится eb02, jmp 28 24 прыжка до 28 28 eax = 2 2а возвращается
Исходя из этого, инструкция на
L1
никогда не должна превращаться вEBBD
(она всегда менялась только наEB00
илиEB02
), поэтому гораздо более вероятно, что у вас есть простая опечатка текста (особенно учитывая ошибку во второмJMP L
, который никогда не изменял ).Думаю, авторы не более совершенны, чем все мы 🙂Язык ассемблера Intel x86 и микроархитектура - безусловные переходы
Пример
jmp a_label; Перейти к a_label jmp bx; Перейти к адресу в BX jmp WORD [указатель]; Перейти к адресу в указателе jmp 7c0h: 0000h; Переход к сегменту 7c0h и смещение 0000h jmp FAR WORD [aFarPointer]; Перейти к сегменту: смещение в aFarPointer
Относительная близость прыжков
jmp a_label
:
- около
Он указывает только смещение части логического адреса пункта назначения.Предполагается, что сегмент -CS
.- родственник
Семантика команды - переход на rel байтов вперед 1 со следующего адреса команды илиIP = IP + rel
.Команда кодируется как
EB
илиEB
, ассемблер выбирает наиболее подходящую форму, обычно предпочитая более короткую.
Возможно переопределение на ассемблере, например, с NASMjmp SHORT a_label
,jmp WORD a_label
иjmp DWORD a_label
генерируют три возможных формы.Абсолютный непрямой ближний прыжок
jmp bx
иjmp WORD [указатель]
являются:
- около
Они указывают только смещение части логического адреса назначения. Предполагается, что сегмент -CS
.- абсолютное косвенное
Семантика инструкций - переход к адресу в reg или mem илиIP = reg
,IP = mem
.Команда кодируется как
FF / 4
, для косвенной памяти размер операнда определяется, как и для любого другого обращения к памяти.Абсолютные дальние прыжки
jmp 7c0h: 0000h
это:
далеко
Он определяет обе части логического адреса : сегмент и смещение.абсолютное Семантика инструкции - переход к сегменту адреса : смещение или
CS = сегмент, IP = смещение
.Команда кодируется как
EA
в зависимости от размера кода.
В некотором ассемблере можно выбирать между двумя формами, например, с помощью NASMjmp 7c0h: WORD 0000h
иjmp 7c0h: DWORD 0000h
сгенерировать первую и вторую форму.Абсолютные непрямые дальние прыжки
jmp ДАЛЬНЕЕ СЛОВО [aFarPointer]
это:
далеко Он определяет обе части логического адреса : сегмент и смещение.
Абсолютное непрямое Семантика инструкции - переход к сегменту : смещение , сохраненное в mem 2 или
CS = mem [23: 16/32], IP = [15/31: 0]
.Инструкция кодируется как
FF / 5
, размер операнда может регулироваться спецификаторами размера.
В NASM, немного не интуитивно понятном, этоjmp FAR WORD [aFarPointer]
для операнда 16:16 иjmp FAR DWORD [aFarPointer]
для операнда 16:32 .Прыжки без прыжков
около абсолютного
Может имитироваться почти непрямым прыжком.mov bx, target; BX = абсолютный адрес цели jmp bx
дальний родственник
В любом случае, это бессмысленно или слишком узко.1 Два дополнения используются для указания смещения со знаком и, таким образом, для перехода назад.
2 Это может быть seg16: off16 или seg16: off32 размеров 16:16 и 16:32 .
Инструкции разветвления и перехода
Инструкции разветвления и переходаВо всех приведенных ниже инструкциях Src2 может быть регистром или регистром. немедленное значение (целое число).Инструкции ветвления используют подписанный 16-битный поле смещения; следовательно, они могут перескакивать инструкции (не байтов) вперед или инструкции назад. Прыжок Инструкция содержит 26-битное адресное поле.
b этикетка
Безусловно переход к инструкции на этикетке.
bc z t этикетка Отраслевой сопроцессор True
bc z f label Branch Coprocessor False
Условно переход к инструкции на этикетке, если сопроцессор флаг условия - истина (ложь).
beq Rsrc1, Src2, labelBranch on Equal
Условно перейти к инструкции на этикетке, если содержимое регистра Rsrc1 равно Src2.
beqz Rsrc, labelBranch on Equal Zero
Условно перейти к инструкции на этикетке, если содержимое Rsrc равно 0.
bge Rsrc1, Src2, labelBranch on Greater Than Equal
bgeu Rsrc1, Src2, labelBranch on GTE Unsigned
Условно перейти к инструкции на этикетке, если содержимое регистра Rsrc1 больше или равно Src2.
bgez Rsrc, labelBranch on Greater Than Equal Zero
Условно перейти к инструкции на этикетке, если содержимое of Rsrc больше или равно 0.
bgezal Rsrc, labelBranch on Greater Than Equal Zero And Link
Условно перейти к инструкции на этикетке, если содержимое of Rsrc больше или равно 0. Сохраните адрес следующая инструкция в регистре 31.
bgt Rsrc1, Src2, labelBranch on Greater Than
bgtu Rsrc1, Src2, labelBranch on Greater Than Unsigned
Условно перейти к инструкции на этикетке, если содержимое регистра Rsrc1 больше Src2.
bgtz Rsrc, labelBranch on Greater Than Zero
Условно перейти к инструкции на этикетке, если содержимое of Rsrc больше 0.
ble Rsrc1, Src2, labelBranch on Less Than Equal
bleu Rsrc1, Src2, labelBranch on LTE Unsigned
Условно перейти к инструкции на этикетке, если содержимое регистра Rsrc1 меньше или равно Src2.
blez Rsrc, labelBranch on Less Than Equal Zero
Условно перейти к инструкции на этикетке, если содержимое of Rsrc меньше или равно 0.
bgezal Rsrc, labelBranch on Greater Than Equal Zero And Link
bltzal Rsrc, labelBranch on Less Than And Link
Условно перейти к инструкции на этикетке, если содержимое of Rsrc больше или равно 0 или меньше 0, соответственно. Сохраните адрес следующей инструкции в регистре 31.
blt Rsrc1, Src2, labelBranch on Less Than
bltu Rsrc1, Src2, labelBranch on Less Than Unsigned
Условно перейти к инструкции на этикетке, если содержимое регистра Rsrc1 меньше Src2.
bltz Rsrc, labelBranch on Less Than Zero
Условно перейти к инструкции на этикетке, если содержимое Rsrc меньше 0.
bne Rsrc1, Src2, labelBranch on Not Equal
Условно перейти к инструкции на этикетке, если содержимое регистра Rsrc1 не равны Src2.
bnez Rsrc, labelBranch on Not Equal Zero
Условно перейти к инструкции на этикетке, если содержимое of Rsrc не равны 0.
j этикетка Перейти
Безоговорочно переходите к инструкции на этикетке.
этикетка jalJump and Link
jalr Rsrc Регистр переходов и ссылок
Безоговорочно перейти к инструкции на метке или адресе которой находится в регистре Rsrc. Сохраните адрес следующего инструкция в регистре 31.Ян Мур 2009-03-11 .
jr RsrcJump Регистр
Безоговорочно перейти к инструкции, адрес которой находится в регистре Rsrc.