Весь Old Programmer здесь: Программирование. Тематическое оглавление моего Zen-канала (Old Programmer). А здесь все по программированию на ассемблере на моем канале. Смотрим также Old Programmer, подводя итоги.
- Список разделов канала Old Programmer, канала о программировании и программистах
Данную статью можно назвать нулевой в отношении других статей по ассемблеру. Такое начало, хотя в статье 1 также дано некоторое введение, особенно касаемо регистров микропроцессора и основам трансляции программ на языке ассемблера. Так что данный материал можно считать дополнение к той статье. Рекомендую также параллельно посмотреть еще и вот эту статью об адресации.
Все изложение попытаюсь построить в виде пунктов, которые дают основы понимания программирования на языке ассемблера.
Основные положения по языку программирования ассемблер
- Язык ассемблера в сущности является машинным языком или языком процессора. Просто вместо числового выражения команд, которые используются процессором, в ассемблер введены их мнемонические обозначения, для удобства программирования. Кроме того, в ассемблере введено понятие метки, чтобы программисту не работать с числовыми абсолютными адресами.
- Язык ассемблера оперирует только с числами целыми или вещественными. Все остальное зависит от интерпретации, которую вы заложили в программу. 64-битовое число может быть просто числом, а может быть адресом. Область памяти, на которую указывает метка, может быть просто последовательностью байтов, а может представлять собой более сложную структуру, которую нужно интерпретировать алгоритмически.
- В ассемблере различают два вида памяти: память регистров (быстрая и не большая), оперативная память (большая и более медленная), доступ к которой осуществляется через метки. Описание регистров смотрим в этой статье. Нас интересуют в первую очередь регистры общего назначения (rax, rbx, rcx, rdx, rdi, rsi, r8, r9, r10, r11, r12, r13, r14, r15).
- Процессор обладает большим набором команд. Но начинающему программировать на ассемблере следует остановится на следующих группах: команды загрузки, арифметические команды, инструкции перехода, логические инструкции, битовые команды. А для начала взять только первые три группы.
- Рассмотрим команды загрузки
Из регистра в регистр: mov %rax, %rbx - данное содержащееся в 64-битовом регистре rax помещается в регистр rbx. Обращаю внимание, что оба регистра должны быть одинаковой разрядности (см. Рисунок 1): mov %al, %r8b - младший байт регистра rax помещается в младший байт регистра r8. Но нельзя так: mov %al, %rbx .
Число в регистр: mov $10000, %rax - число 10000 помещается в регистр rax. Можно помещать число в младшие части регистров, например, mov $100, %ax. Знак '$' обязателен перед числом, в противном случае число трактуется как адрес содержимого памяти, которое следует поместить в регистр.
Из памяти в регистр и обратно : mov qw, %rax - помещает 8-байтовое число, расположенное по адресу qw в регистр %rax. Или mov %rax, lbl - помещает 8-байтовое число из регистра rax в память, на которую указывает метка lbl.
Косвенная адресация (подробнее здесь и здесь): mov (%rax), %rdi - поместить данное, адрес которого содержится в rax в регистр rdi.
6. Арифметические команда (подробнее об арифметических операциях см. здесь). Основные арифметические операции add и sub ведут себя подобно командам пересылки. Например sub qw, %rdi - из регистра rdi вычитается число (64-битовое), на которое указывает qw и помещает в регистр rdi. Выбор разрядности числа в памяти определяется разрядностью регистра. С умножением и делением все несколько сложнее. Если сложение и вычитание не зависит от того, рассматриваем мы число знаковым или нет, то для умножения и деления приходится выбирать команду для знакового или беззнакового действия. Например, mul r8 - умножить содержимое регистра r8 на содержимое регистра rax, а результат положить в пару регистров rdx:rax. Для знакового умножения следует использовать команду imul. Вообще структуре чисел и знаковой арифметике мне придется посвятить отдельную статью.
7. Инструкции перехода. Существуют два типа переходов: безусловный переход (jmp и call) и условный переход (jxxx и loop). Условный переход выполняется если выполнено некоторое условие. Условие это связано с последней арифметической операцией. Например
sub rax,rbx
ja lo
вычесть содержимое rbx из содержимого rax и перейти на метку lo, если число, которое было в raxбольше содержимого регистра rbx. Есть важная команда cmp, которая ведет себя точно также как sub, но при этом содержимое регистра не меняется.
Пример программы на языке ассемблера
В заключении статьи рассмотрим следующую программу (600.s). Мы максимально ее упростили, в частности она не выводит ничего на консоль. Об этом можно посмотреть в других моих статьях по ассемблеру. Откомпилировать программу можно командами:
as --64 600.s -o 600.o
ld -s -e main 600.o -o 600
В следующей статье речь пойдет о числах в ассемблере. Знаковых и беззнаковых, как они хранятся и т.д.
Ассемблер требует скрупулезности, но в нем вы взаимодействуете с системой напрямую, что вызывает самоуважение. Пока! Подписываемся на мой канал Old Programmer.
Я вижу, что вы забыли поставить ЛАЙК, не так ли?