Это старая версия документа!
Ветвления
Ветвления в ассемблере: архитектуры x86 (Intel), ARM, MIPS
1. Основные функции ветвления в ассемблере
Ветвление (branching) — это переход исполнения программы на другую инструкцию, обычно по условию. Вот основные виды ветвления (на примере x86):
Безусловный переход: :: JMP label
Условный переход по флагам: :: Jcc label (JE, JNE, JL, JG и др.)
Переход с возвратом: :: CALL label, RET
Циклические переходы: :: LOOP label, LOOPE, LOOPNE
Косвенные переходы: :: JMP [reg]
Табличные переходы: :: через адресацию перехода по таблице (jump table)
Прерывания и возврат из них: :: INT N, IRET
Желающие подробности по архитектурам — читайте далее.
2. x86 (Intel)
2.1. Работа флагов
После арифметической/логической операции процессор выставляет флаги в регистре EFLAGS:
| Название | Описание | ZF (Zero Flag) | Результат 0? (1 если да) | SF (Sign Flag) | Знак результата (1=минус) | CF (Carry Flag) | Перенос (важно при беззнаковых) | OF (Overflow Flag) | Переполнение (важно при знаковых) | PF (Parity Flag) | Четность (не часто используется) |
|---|
2.2. Основные команды ветвления
| Инструкция | Условие | Обозначение | JE/JZ | ZF = 1 | Равно («Jump if Equal») | JNE/JNZ | ZF = 0 | Не равно | JL | (SF ≠ OF) | Меньше (signed) | JLE | (ZF=1 or SF ≠ OF) | Меньше или равно (signed) | JG | (ZF=0 and SF=OF) | Больше (signed) | JGE | (SF=OF) | Больше или равно (signed) | JB | CF = 1 | Меньше (unsigned) | JBE | CF=1 or ZF=1 | Меньше или равно (unsigned) | JA | (CF=0 and ZF=0) | Больше (unsigned) | JAE | CF = 0 | Больше или равно (unsigned) | CALL, RET | Вызов и возврат процедуры | JMP [рег] | Косвенный переход |
|---|
2.3. Примеры кода и пояснения
==== Безусловный переход ====
JMP somewhere
Условный переход и работа флагов
mov eax, -3 ; eax = FFFFFFFDh (32-битный -3) mov ebx, 1 ; ebx = 00000001h cmp eax, ebx ; вычитание: -3 - 1 = -4 (0xFFFFFFFCh) jg more_label ; если eax > ebx (signed)? jl less_label ; если eax < ebx (signed)? je equal_label ; если eax == ebx? mov ecx, 123 jmp end more_label: mov ecx, 1 jmp end less_label: mov ecx, 2 jmp end equal_label: mov ecx, 3 end:
Анализ флагов:
После 'cmp eax, ebx':
ZF = 0 (результат не ноль)
SF = 1 (отрицательное)
OF = 0 (переполнения нет)
CF = 1 (был «заём» беззнаково)
'jg': требует ZF=0 и SF=OF (1 ≠ 0 – не равно, перехода нет)
'jl': SF ≠ OF? (1 ≠ 0 — переход будет!)
==== Проверка на равенство ====
mov eax, 7 mov ebx, 7 cmp eax, ebx je eq_label ; ZF=1, переход будет jne neq_label ; ZF=0, переход не будет
==== Цикл ====
mov cx, 5 loop_start: ; ... тело цикла loop loop_start
==== Вызов функции ====
call my_function ; ... my_function: ; действия ret
==== Косвенный переход ====
jmp [ebx] ; ebx — адрес новой инструкции
3. ARM
3.1. Флаги
| Флаг | Назначение | N (Negative) | Результат отрицательный? (MLSB=1) | Z (Zero) | Результат 0? | C (Carry) | Перенос/заём для беззнаковых | V (Overflow) | Переполнение для знаковых |
|---|
Инструкции, которые изменяют флаги — обычно с S: 'ADDS', 'SUBS', 'CMP', 'ANDS' и др.
3.2. Ветвления по суффиксам
Любая инструкция ветвления (и почти любая другая) может иметь суффикс условия:
| Суффикс | Условие | Описание | EQ | Z=1 | Равно | NE | Z=0 | Не равно | LT | N ≠ V | Меньше (signed) | GT | Z=0 и N=V | Больше (signed) | GE | N=V | Больше или равно (signed) | LE | Z=1 или N ≠ V | Меньше или равно (signed) | CS/HS | C=1 | Беззнаково >= | CC/LO | C=0 | Беззнаково < |
|---|
3.3. Примеры кода и пояснения
==== Пример: ====
MOV R0, #5 MOV R1, #8 CMP R0, R1 ; R0 - R1 = -3 (Negative) BEQ label_eq ; Z=1 ? BNE label_ne ; Z=0 ? BLT label_lt ; N ≠ V ?
После CMP:
N = 1 (отрицательное) Z = 0 (не ноль) V = 0 C = 0 BEQ — не сработает (Z=0)
BNE — выполнится (Z=0)
BLT — N ≠ V (1 ≠ 0) — выполнится
==== Вызовы процедур ====
BL myfunc ; вызов с занесением адреса возврата в LR ; ... myfunc: ; ... BX LR ; возврат
==== Табличный переход ====
CMP R0, #3 BHI default LDR PC, [PC, R0, LSL #2] ; PC ← (таблица адресов: case0, case1...) .word case0 .word case1 .word case2 .word case3 case0: ; ... BX LR default:
4. MIPS
4.1. Нет регистра флагов!
Всё ветвление идёт по значению регистров.
| Инструкция | Что выполняет |
|---|
a , a,b, label | Переход, если равны ( a = a=b) | | bne a , a,b, label | Переход, если не равны | | slt r , r,a, b ∣ b∣r = 1, если a < a<b (signed), иначе 0 | | sltu r , r,a, b ∣ b∣r = 1, если a < a<b (unsigned) | | jal, jr $ra | Вызов/возврат процедуры |
4.2. Пример кода и разбор
==== Знаковое сравнение ====
li t 0 , − 2 l i t0,−2lit1, 1 slt a t , at,t0, t1 # at = ( t 0 < t0<t1) ? 1 : 0 bnez at, less_label # если at != 0 — переход будет
==== Вызов функции ====
jal my_func ... my_func: ; ... jr $ra
==== Табличный переход ====
sll t 1 , t1,a0, 2 la t 2 , j u m p t a b l e a d d u t2,jumptableaddut1, t 1 , t1,t2 lw t 3 , 0 ( t3,0(t1) jr $t3
5. Краткий итог сравнения ветвлений
x86 EFLAGS (ZF, SF, OF, CF, PF) JE, JNE, JL, JG, JA, JB… cmp ax, bx; jl label ARM CPSR (N, Z, C, V) B<cond> (BEQ, BNE, BLT и др.) cmp r0, r1; blt label MIPS Нет флагов, сравн. в регистре beq, bne, slt+bnez slt a t , at,a0, a 1 ; b n e z a1;bnezat, l
6. Разбор переходов с анализом флагов (на примере x86, ARM, MIPS)
6.1. x86: знаковое сравнение
mov eax, -3 ; FFFFFFFD mov ebx, 1 ; 00000001 cmp eax, ebx ; -3 - 1 = -4 (FFFFFFFC) jg more_label ; больше? (signed) jl less_label ; меньше? (signed) je equal_label ; равно?
Флаги:
ZF = 0 (результат не ноль)
SF = 1 (отрицательное)
OF = 0 (нет переполнения)
CF = 1 (беззнаковый «заём»)
'JG' требует ZF=0 и SF=OF: 1 ≠ 0 — перехода НЕ будет.
'JL' требует SF ≠ OF: 1 ≠ 0 — переход БУДЕТ!
=== 6.2. x86: проверка на равенство ===
mov eax, 7 mov ebx, 7 cmp eax, ebx je eq_label ; ZF=1 — переход БУДЕТ! jne neq_label ; ZF=0 — перехода не будет
6.3. ARM: сравнение и разбор по флагам
MOV R0, #5 MOV R1, #8 CMP R0, R1 ; результат -3, N=1, Z=0, V=0 BEQ label_eq ; Z=1? BNE label_ne ; Z=0? BLT label_lt ; N ≠ V?
После CMP:
N = 1 (отрицательно), V = 0, Z = 0 BEQ — НЕ сработает; BNE — переход БУДЕТ; BLT — переход БУДЕТ. === 6.4. ARM: переполнение ===
MOV R2, #0x7FFFFFFF ADD R2, R2, #1 ; R2 = 0x80000000 (отрицательное по знаку) CMP R2, #0 BEQ label_zero ; Z=0 BMI label_negative ; N=1 — БУДЕТ переход! BVS label_o ; V=1, было переполнение — БУДЕТ переход!
=== 6.5. MIPS: ветвление через регистр-посредник ===
li t 0 , − 2 l i t0,−2lit1, 1 slt a t , at,t0, t1 # if t0 < t 1 , t1,at = 1 bnez $at, less_label # если 1 — переход БУДЕТ!