====== Ветвления ====== ====== Ветвления в ассемблере: архитектуры 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) | Результат отрицательный? (MSB=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. Нет регистра флагов! === Всё ветвление идёт по значению регистров. ^ Инструкция | Что выполняет | | beq $a, $b, label | Переход, если равны ($a = $b) | | bne $a, $b, label | Переход, если не равны | | slt $r, $a, $b | $r = 1, если $a < $b (signed), иначе 0 | | sltu $r, $a, $b | $r = 1, если $a < $b (unsigned) | | jal, jr $ra | Вызов/возврат процедуры | === 4.2. Пример кода и разбор === ==== Знаковое сравнение ==== li $t0, -2 li $t1, 1 slt $at, $t0, $t1 # $at = ($t0 < $t1) ? 1 : 0 bnez $at, less_label # если $at != 0 — переход будет ==== Вызов функции ==== jal my_func # ... my_func: ; ... jr $ra ==== Табличный переход ==== sll $t1, $a0, 2 la $t2, jumptable addu $t1, $t1, $t2 lw $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 (BEQ, BNE, BLT и др.) | cmp r0, r1; blt label | | MIPS | Нет флагов, сравн. в регистре | beq, bne, slt+bnez | slt $at, $a0, $a1; bnez $at, 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 $t0, -2 li $t1, 1 slt $at, $t0, $t1 # if $t0 < $t1, $at = 1 bnez $at, less_label # если 1 — переход БУДЕТ! ---