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

О языке программирования ассемблер на платформе x86-64. Самое начало

Оглавление

Весь Old Programmer здесь: Программирование. Тематическое оглавление моего Zen-канала (Old Programmer). А здесь все по программированию на ассемблере на моем канале. Смотрим также Old Programmer, подводя итоги.

  • Список разделов канала Old Programmer, канала о программировании и программистах

Данную статью можно назвать нулевой в отношении других статей по ассемблеру. Такое начало, хотя в статье 1 также дано некоторое введение, особенно касаемо регистров микропроцессора и основам трансляции программ на языке ассемблера. Так что данный материал можно считать дополнение к той статье. Рекомендую также параллельно посмотреть еще и вот эту статью об адресации.

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

Основные положения по языку программирования ассемблер

  1. Язык ассемблера в сущности является машинным языком или языком процессора. Просто вместо числового выражения команд, которые используются процессором, в ассемблер введены их мнемонические обозначения, для удобства программирования. Кроме того, в ассемблере введено понятие метки, чтобы программисту не работать с числовыми абсолютными адресами.
  2. Язык ассемблера оперирует только с числами целыми или вещественными. Все остальное зависит от интерпретации, которую вы заложили в программу. 64-битовое число может быть просто числом, а может быть адресом. Область памяти, на которую указывает метка, может быть просто последовательностью байтов, а может представлять собой более сложную структуру, которую нужно интерпретировать алгоритмически.
  3. В ассемблере различают два вида памяти: память регистров (быстрая и не большая), оперативная память (большая и более медленная), доступ к которой осуществляется через метки. Описание регистров смотрим в этой статье. Нас интересуют в первую очередь регистры общего назначения (rax, rbx, rcx, rdx, rdi, rsi, r8, r9, r10, r11, r12, r13, r14, r15).
  4. Процессор обладает большим набором команд. Но начинающему программировать на ассемблере следует остановится на следующих группах: команды загрузки, арифметические команды, инструкции перехода, логические инструкции, битовые команды. А для начала взять только первые три группы.
  5. Рассмотрим команды загрузки

Из регистра в регистр: 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, но при этом содержимое регистра не меняется.

Рисунок 1. Воспользовался картинкой, присланной мне одним из комментаторов. Спасибо ему большое.  Очень хорошо показывает доступ к разным частям регистров.
Рисунок 1. Воспользовался картинкой, присланной мне одним из комментаторов. Спасибо ему большое. Очень хорошо показывает доступ к разным частям регистров.

Пример программы на языке ассемблера

В заключении статьи рассмотрим следующую программу (600.s). Мы максимально ее упростили, в частности она не выводит ничего на консоль. Об этом можно посмотреть в других моих статьях по ассемблеру. Откомпилировать программу можно командами:

as --64 600.s -o 600.o
ld -s -e main 600.o -o 600

В следующей статье речь пойдет о числах в ассемблере. Знаковых и беззнаковых, как они хранятся и т.д.

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

Фрагмент программы 600.s
Фрагмент программы 600.s

Я вижу, что вы забыли поставить ЛАЙК, не так ли?