Циклы на ассемблере

Урок собирает циклы из уже знакомых деталей: метки, счётчика и условного перехода.

Цикл в ассемблере — это участок кода, в конец которого добавлен переход назад при невыполненном условии выхода.

Анатомия цикла

Любой цикл состоит из четырёх частей: инициализация счётчика, проверка условия, тело и переход назад. Посчитаем сумму чисел от 1 до 5:

    mov rax, 0      ; сумма = 0
    mov rcx, 1      ; счётчик i = 1
loop_start:
    cmp rcx, 5      ; пока i <= 5
    jg  loop_end    ; если i > 5, выйти
    add rax, rcx    ; сумма += i
    inc rcx         ; i++
    jmp loop_start  ; назад к проверке
loop_end:
    ; в rax теперь 15

Это в точности цикл for (i=1; i<=5; i++) sum += i; из C, разложенный на машинные шаги.

Команда loop как сокращение

x86 предлагает специальную команду loop, которая уменьшает rcx и прыгает, пока он не ноль:

    mov rcx, 5      ; повторить 5 раз
repeat:
    ; ... тело ...
    loop repeat     ; rcx-- ; если rcx != 0, прыгнуть в repeat

Это компактно, но loop жёстко завязан на rcx и в современном коде используется реже, чем явная пара dec/jnz.

Как работает под капотом

Промоделируем тот же цикл суммирования на Python — он повторяет ассемблерную логику шаг в шаг:

rax = 0          # сумма
rcx = 1          # счётчик
while rcx <= 5:  # cmp rcx,5 ; jg loop_end
    rax += rcx   # add rax, rcx
    rcx += 1     # inc rcx
print("сумма 1..5 =", rax)

Вывод:

сумма 1..5 = 15

Процессор не знает слова while — он просто прыгает назад, пока счётчик не дойдёт до границы. Любой цикл любого языка в итоге компилируется именно в такую конструкцию из сравнения и перехода.

Частые ошибки

  • Забыть менять счётчик. Без inc rcx условие выхода никогда не выполнится — вечный цикл.
  • Перепутать границу. jg против jge даёт цикл на одну итерацию короче или длиннее.
  • Затереть rcx командой mul/div внутри цикла. Эти команды используют rcx/rdx и собьют счётчик.

Итог

  • Цикл = инициализация + проверка + тело + переход назад.
  • Условный переход в начале и jmp в конце образуют петлю.
  • Команда loop — компактная, но завязана на rcx.
  • Все циклы высокого уровня компилируются в сравнение плюс переход.
Проверьте себя
1. Из каких частей состоит цикл в ассемблере?
AТолько тело
BИнициализация, проверка условия, тело и переход назад
Ccmp и ret
DМетка и mov
2. Что делает команда loop repeat?
AПрыгает всегда
BУменьшает rcx и прыгает в repeat, пока rcx не ноль
CУвеличивает rcx
DОчищает rcx
3. Что произойдёт, если в цикле забыть inc rcx?
AЦикл выполнится один раз
BУсловие выхода никогда не сработает — вечный цикл
CПрограмма не соберётся
Drcx обнулится сам