Найти в Дзене
Old Programmer

Ассемблер, команды ассемблера, основные конструкции (assembler x86-64)

Здравствуйте, это канал Old Programmer и снова о программировании и только о программировании. Сегодня опять ассемблер , а здесь обзор материалов моего канала только по языку ассемблер
Язык ассемблера (x86-64, Linux). Путеводитель по ресурсам канала Old Programmer
Предыдущая статья по ассемблеру
GNU assembler x86-64 Linux. Стек и команды ассемблера
Оглавление

Здравствуйте, это канал Old Programmer и снова о программировании и только о программировании. Сегодня опять ассемблер, а здесь обзор материалов моего канала только по языку ассемблер

  • Язык ассемблера (x86-64, Linux). Путеводитель по ресурсам канала Old Programmer

Предыдущая статья по ассемблеру

  • GNU assembler x86-64 Linux. Стек и команды ассемблера

Как я уже неоднократно писал, изначально я не задумывал писать об ассемблере подробно. Хотел лишь отдельные темы осветить. Но потом понял, что хочу сделать книгу. И уже постфактум начал дополнять материалы по ассемблеру вопросами, делающими все изложение более полным. Так что сегодня одна из статей, которую нужно было раньше публиковать. Но лучше поздно, чем никогда.

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

Условные конструкции в языке ассемблер

Основная идея условных конструкций на ассемблере, это результат выполнения каких либо операций. На ассемблере есть целый набор "переходов по условию". В микропроцессоре есть регистр флагов (FLAGS), содержимое которого меняется в зависимости результата выполнения некоторых операций. Вот эти флаги и влияют на то, как выполняются команды переходов по условию.

...
sub %rax, %rbx
jz l2
...
l2:
...

В данном фрагменте происходит переход на метку l2, при условии, что в результате выполненной операции вычитания в rbx оказалось число 0. Если использовать команду jnz, то переход будет осуществляться если результат не 0. Если мы не хотим реально менять содержимое регистров, то вместо sub используется команда cmp, которая действие не выполняет, но меняет содержимое регистра флагов так же как sub:

...
cmp %rax, %rbx
jz l2
...
l2:
...

Есть набор команд, позволяющий сравнивать операнды на предмет "больше - меньше":

...
cmp %rax, %rbx
jge l2
...
l2:
...

В данном фрагмент переход на метку l2 происходит только в том случае. если содержимое rbx больше или равно содержимого rax.

До сих пор мы приводили примеры так сказать не полных условных конструкций. Но реализовать полную условную конструкцию совсем не сложно:

...
cmp %rax, %rbx
jz l2
# else
...
jmp l1
l2:
# if
...
l1:
...

Как мы видим, для этого нам понадобилась команда безусловного перехода jmp.

Как видим, все просто. Ну и осталось посмотреть, как можно организовать множественный выбор.

...
cmp $100, %rax
jnz l1
... # x==100
jmp ll
l1:
cmp $200, %rax
jnz l2
... # x==200
jmp ll
l2:
cmp $300, %rax
jnz l3
... # x == 300
jmp ll
l3:
... # else
ll:
...

Как видим очень просто. Ну, конечно, приходится использовать команды переходов, которые мы так не любят в языках высокого уровня любители чистого программирования.

Циклы в языке ассемблер

Обратимся теперь к тому, как можно организовывать циклы. Ниже представлен типичный цикл "ДО".

...
mov $10, %rdx
push %rdx
l1:
... # тело цикла
pop %rdx
sub $1, %rdx
push %rdx
jnz l1
pop %rdx

...

В конце выполнения тела цикла проверяется значение счетчика, и если счетчик еще не равен нулю, то цикл продолжается. Важно понимать, что если мы храним счетчик в регистре (у нас это rdx), то в результате выполнения тела цикла он может не сохранится. Для его сохранности мы используем стек. Надо также помнить, что если используете системные вызовы то как минимум содержимое rcx и r11 у вас не сохраняется. Я уж не говорю о использовании функций сторонних библиотек. Ну и более коротки вариант цикла "ДО" представлен ниже

...
mov $10, %rdx
l1:
push %rdx
... # тело цикла
pop %rdx
sub $1, %rdx
jnz l1
...

А вот типичный цикл "ПОКА".

...
mov $10, %rdx
l1:
cmp $0, %rdx
jz l2
push %rdx
... # тело цикла
pop %rdx
sub $1, %rdx
jmp l1
l2:
pop %rdx
...

Как видим, для организации цикла "ПОКА" понадобилась более сложная программная структура.

Ну и наконец, отметим важную команду loop, которая часто используется для организации цикла. Команда уменьшает содержимое регистра rcx на единицу и если содержимое не 0, осуществляет переход на указанный адрес (метку).

...
mov $10, %rcx
l1:
push %rcx
... # тело цикла
pop %rcx
loop l1
...

Как видим, loop делает цикл более компактным.

Дабы не увеличивать размеры статьи мы не стали рассматривать сегодня рассматривать еще два вопроса: сложные условия и вложенные циклы. Сделаем это в следующих статьях. Кроме этого в одной из следующих статей я дам справочный материал по условным переходам и флагам.

Ассемблер на нашем канале Old Programmer продолжается. Подписывайтесь на мой канал, ставьте лайки и комментируйте.

Assembler forever
Assembler forever