mov Программирование на Ассемблере без знаний Ассемблера, habr

от автора

Пролог

Решил поделиться своими мыслями и кратким двухдневным опытом написания (собирания по частям) программы на Ассемблере без чтения учебников, больших статей и в целом без опыта программирования на Ассемблере. На одном из форумов я набрёл на задачу вывода десятичного числа в консоль. Если на языке C или PHP эта операция совершенно элементарна, то на Ассемблере всё не так просто, как может показаться на первый взгляд. Для решения задачи я выбрал nasm (правда, выбора и не было), немножко поигравшись предварительно с вставками nasm (синтаксис AT&T) в код C (ссылка на форум с моими опытами в конце статьи).

Философское отступление
Остановись, дорогой читатель! И прежде чем читать дальше задай себе вопрос: возможно ли начать ковать без обучения кузнечному делу?!

Я оставлю этот вопрос без ответа. Только скажу, что в нём нет ни капли иронии, издёвки, намёка на назидательность и т.д. Это вопрос без какого-либо дополнительного подтекста.

Поиски
Информации по Ассемблеру в Интернете очень много и заблудиться в разных видах Ассемблера (для различных систем) крайне просто. Я не единственный задавался вопросом в поиске «how to print a number in asm». Ответы на разных диалектах языка относительно легко можно найти на Stack Overflow, однако это совершенно не означает, что будет легко запустить найденный код на своей машине. Велика вероятность того, что что-нибудь не сойдётся. Научиться отличать синтаксис AT&T и intel можно за несколько минут, а вот с узнаванием tasm, fasm, masm, nasm — несколько сложнее. Единственное, что можно предположить и (почти) не прогадать: базовые инструкции во всех Ассемблерах имеют (почти) одинаковые мнемоники.

Ассоциации и первые впечатления

Если хотите, чтобы ваши соседи переехали, убедите себя в том, что играть на скрипке легко…

Самая распространённая операция — сдвиг одного регистра в другой — лично у меня ассоциируется с перемещением фигур на шахматной доске.
В этом смысле язык кажется лёгким, даже очень лёгким или лучше сказать удобным.
Хотя, как известно, и правила шахмат нельзя назвать слишком сложными, но играть бывает крайне трудно.

Итак, задача:
Собрать программу, печатающую на стандартный вывод (stdout) число.

Реализация
Принцип (его надо мысленно прокрутить в голове и понять, тогда можно считать, что мы не списываем):

  1. Число делится на 10.

  2. В регистр edx заносится остаток, в eax остальная часть.

  3. Остаток записывается в буфер (stroka), начиная с конца, используется декремент, затем выводится

  4. Дополнительно: при использовании инкремента можно напечатать строку в обратном порядке.

Код nasm (собран на архитектуре 64b, с моими и чужими комментариями)

section .data   stroka db 4;буфер для вывода section .text  global _start  ;must be declared for using gcc  _start:            ;tell linker entry point push rbp       ;Работа со стеком mov rbp, rsp mov eax, 311  ; 311 - делимое mov edi, 3      ; Переменная цикла. loop:               ; Начало цикла       dec edi  ; Декремент, чтобы  записать все значения остатка в stroka     mov ebx, 10      ; Делитель. Запишем  в цикл, чтобы вернуть ему значение на новой итерации     xor edx, edx     ; Обнулим остаток     div ebx          ; Делим     add edx, 30h     ; Добавим в остаток 0, равносильно add edx, '0'     mov  [stroka + edi], dl ; Пишем в строку остаток в обратном порядке.     cmp edi,  0       ; Выходим jne loop              ; Возврат в цикл                          ; Собрали строку и далее выводим    mov ecx, stroka   ; Кладём     mov     edx, 4      ; Длина выводимой строки  4 (видимо, в байтах)     mov     ebx, 1      ; file descriptor (stdout)     mov     eax, 4      ; system call number (sys_write)     int     0x80         ; call kernel      mov     eax, 1      ;system call number (sys_exit)     int     0x80        ;call kernel 

Команда для сборки, линковки и выполнения

 nasm -f elf64 print_n.asm -o print_n.o; ld -o print_n print_n.o; ./print_n

Оставлю для таких же как я дополнительную задачку и подсказку.

Задача: изменить программу так, чтобы число печаталось наоборот.

Подсказка

Hidden text

Менять нужно mov edi, 3; dec edi; cmp edi, 0

Дополнительно: как сделать из нашего hello world что-нибудь полезное?

Если немножко почитать про стек, а также регистры rsp, rbp и поиграть со смещением, то можно быстро переделать программу в генератор псевдослучайных чисел:

mov rbp, rsp mov eax, [rbp +17]  ; Забираем из стека по адресу 17 значение add eax, [rbp +18]  ; И ещё сместимся на 1 (видимо, бит) и добавим в eax

Далее самописный псевдогенератор

section .data   stroka db 4 ;буфер для вывод section .text  global _start   ;must be declared for using gcc  _start:            ;tell linker entry point push rbp       ;Работа со стеком mov rbp, rsp mov eax, [rbp +17]  ; Забираем из стека по адресу 17 значение add eax, [rbp +18]  ; И ещё сместимся 1 одно значение, добавим в eax mov edi, 3         ; Переменная цикла. loop:                  ; Начало цикла       dec edi  ;Декремент, чтобы  записать все значения остатка в stroka     mov ebx, 10      ; Делитель. Запишем  в цикл, чтобы вернуть ему значение на новой итерации     xor edx, edx     ; Обнулим остаток     div ebx          ; Делим     add edx, 30h     ; Добавим в остаток 0, равносильно add edx, '0'     mov  [stroka + edi], dl ; Пишем в строку остаток в обратном порядке.     cmp edi,  0          ; Выходим jne loop                ; Возврат в цикл                           ; Собрали строку и далее выводим    mov ecx, stroka     ; Кладём     mov     edx, 4      ; Длина выводимой строки  4 (видимо, в байтах)     mov     ebx, 1      ; file descriptor (stdout)     mov     eax, 4      ; system call number (sys_write)     int     0x80        ; call kernel      mov     eax, 1      ;system call number (sys_exit)     int     0x80        ;call kernel 

Источники:

1) https://acm.mipt.ru/twiki/bin/view/Asm/PrintIntFunction

2) http://av-assembler.ru/asm/afd/asm-cpu-registers.htm

3) https://wasm.in/


ссылка на оригинал статьи https://habr.com/ru/post/707862/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *