Циклы на ассемблере
Урок собирает циклы из уже знакомых деталей: метки, счётчика и условного перехода.
Цикл в ассемблере — это участок кода, в конец которого добавлен переход назад при невыполненном условии выхода.
Анатомия цикла
Любой цикл состоит из четырёх частей: инициализация счётчика, проверка условия, тело и переход назад. Посчитаем сумму чисел от 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. - Все циклы высокого уровня компилируются в сравнение плюс переход.