Учебники

Сборка — Краткое руководство

Сборка — Введение

Каждый персональный компьютер имеет микропроцессор, который управляет арифметическими, логическими и управляющими действиями компьютера.

Каждое семейство процессоров имеет свой собственный набор инструкций для обработки различных операций, таких как получение ввода с клавиатуры, отображение информации на экране и выполнение различных других заданий. Этот набор инструкций называется «инструкции машинного языка».

Процессор понимает только инструкции машинного языка, которые представляют собой строки 1 и 0. Однако машинный язык слишком неясен и сложен для использования при разработке программного обеспечения. Таким образом, низкоуровневый ассемблер разработан для определенного семейства процессоров, которые представляют различные инструкции в символическом коде и в более понятной форме.

Преимущества ассемблера

Знание языка ассемблера позволяет понять:

  • Как программы взаимодействуют с ОС, процессором и BIOS;
  • Как данные представлены в памяти и других внешних устройствах;
  • Как процессор обращается к инструкции и выполняет ее;
  • Как инструкции обращаются и обрабатывают данные;
  • Как программа обращается к внешним устройствам.

Другие преимущества использования ассемблера —

  • Это требует меньше памяти и времени выполнения;

  • Это упрощает сложные аппаратные задачи;

  • Подходит для срочных работ;

  • Он наиболее подходит для написания подпрограмм обработки прерываний и других резидентных программ.

Это требует меньше памяти и времени выполнения;

Это упрощает сложные аппаратные задачи;

Подходит для срочных работ;

Он наиболее подходит для написания подпрограмм обработки прерываний и других резидентных программ.

Основные характеристики аппаратного обеспечения ПК

Основное внутреннее оборудование ПК состоит из процессора, памяти и регистров. Регистры являются компонентами процессора, которые содержат данные и адрес. Для выполнения программы система копирует ее с внешнего устройства во внутреннюю память. Процессор выполняет инструкции программы.

Фундаментальная единица компьютерного хранения — немного; это может быть ВКЛ (1) или ВЫКЛ (0). Группа из девяти связанных битов составляет байт, из которых восемь бит используются для данных, а последний используется для контроля четности. Согласно правилу четности, количество битов, которые включены (1) в каждом байте, всегда должно быть нечетным.

Таким образом, бит четности используется для того, чтобы сделать количество битов в байте нечетным. Если четность четная, система предполагает, что произошла ошибка четности (хотя и редкая), которая могла быть вызвана неисправностью оборудования или электрическими помехами.

Процессор поддерживает следующие размеры данных —

  • Word: 2-байтовый элемент данных
  • Doubleword: 4-байтовый (32-битный) элемент данных
  • Quadword: 8-байтовый (64-битный) элемент данных
  • Абзац: 16-байтовая (128-битная) область
  • Килобайт: 1024 байта
  • Мегабайт: 1 048 576 байт

Двоичная система счисления

В каждой системе счисления используются позиционные обозначения, т. Е. Каждая позиция, в которой записана цифра, имеет различное позиционное значение. Каждая позиция — это мощность базы, которая равна 2 для двоичной системы счисления, и эти мощности начинаются с 0 и увеличиваются на 1.

В следующей таблице приведены позиционные значения для 8-битного двоичного числа, где все биты установлены в положение ON.

Значение бита 1 1 1 1 1 1 1 1
Значение позиции как степень основания 2 128 64 32 16 8 4 2 1
Номер бита 7 6 5 4 3 2 1 0

Значение двоичного числа основано на наличии 1 бита и их позиционном значении. Таким образом, значение данного двоичного числа —

1 + 2 + 4 + 8 +16 + 32 + 64 + 128 = 255

что так же, как 2 8 — 1.

Шестнадцатеричная система счисления

Шестнадцатеричная система счисления использует основание 16. Цифры в этой системе варьируются от 0 до 15. По соглашению, буквы от A до F используются для представления шестнадцатеричных цифр, соответствующих десятичным значениям с 10 по 15.

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

Десятичное число Двоичное представление Шестнадцатеричное представление
0 0 0
1 1 1
2 10 2
3 11 3
4 100 4
5 101 5
6 110 6
7 111 7
8 1000 8
9 1001 9
10 1010
11 1011 В
12 1100 С
13 1101 D
14 1110 Е
15 1111 F

Чтобы преобразовать двоичное число в его шестнадцатеричный эквивалент, разбейте его на группы по 4 последовательные группы в каждой, начиная справа, и запишите эти группы поверх соответствующих цифр шестнадцатеричного числа.

Пример — двоичное число 1000 1100 1101 0001 эквивалентно шестнадцатеричному — 8CD1

Чтобы преобразовать шестнадцатеричное число в двоичное, просто запишите каждую шестнадцатеричную цифру в ее 4-значный двоичный эквивалент.

Пример — шестнадцатеричное число FAD8 эквивалентно двоичному — 1111 1010 1101 1000

Двоичная арифметика

Следующая таблица иллюстрирует четыре простых правила для двоичного сложения —

(я) (II) (III) (IV)
1
0 1 1 1
+0 +0 +1 +1
= 0 = 1 = 10 = 11

Правила (iii) и (iv) показывают перенос 1 бита в следующую левую позицию.

пример

Десятичный двоичный
60 00111100
+42 00101010
102 01100110

Отрицательное двоичное значение выражается в двоичной записи . Согласно этому правилу преобразовать двоичное число в его отрицательное значение означает инвертировать его битовые значения и добавить 1 .

пример

Номер 53 00110101
Поменять местами 11001010
Добавить 1 0000000 1
Номер -53 11001011

Чтобы вычесть одно значение из другого, преобразуйте вычитаемое число в формат дополнения до двух и добавьте числа .

пример

Вычтите 42 из 53

Номер 53 00110101
Номер 42 00101010
Обратный бит 42 11010101
Добавить 1 0000000 1
Номер -42 11010110
53 — 42 = 11 00001011

Переполнение последнего 1 бита потеряно.

Адресация данных в памяти

Процесс, посредством которого процессор управляет выполнением инструкций, называется циклом выборки-декодирования-выполнения или циклом выполнения . Он состоит из трех непрерывных шагов —

  • Извлечение инструкции из памяти
  • Расшифровка или идентификация инструкции
  • Выполнение инструкции

Процессор может одновременно обращаться к одному или нескольким байтам памяти. Давайте рассмотрим шестнадцатеричное число 0725H. Для этого числа потребуется два байта памяти. Байт старшего разряда или старший значащий байт — 07, а младший байт — 25.

Процессор хранит данные в последовательности обратных байтов, то есть байт младшего разряда сохраняется в адресе с низким объемом памяти, а байт старшего разряда в адресе с высоким объемом памяти. Таким образом, если процессор переносит значение 0725H из регистра в память, он сначала перенесет 25 на нижний адрес памяти и 07 на следующий адрес памяти.

Вступление

х: адрес памяти

Когда процессор получает числовые данные из памяти для регистрации, он снова переворачивает байты. Есть два вида адресов памяти —

  • Абсолютный адрес — прямая ссылка на конкретное место.

  • Адрес сегмента (или смещение) — начальный адрес сегмента памяти со значением смещения.

Абсолютный адрес — прямая ссылка на конкретное место.

Адрес сегмента (или смещение) — начальный адрес сегмента памяти со значением смещения.

Сборка — Настройка среды

Настройка локальной среды

Язык ассемблера зависит от набора команд и архитектуры процессора. В этом руководстве мы сосредоточимся на процессорах Intel-32, таких как Pentium. Чтобы следовать этому уроку, вам понадобится —

  • ПК IBM или любой другой совместимый компьютер
  • Копия операционной системы Linux
  • Копия программы ассемблера NASM

Есть много хороших ассемблерных программ, таких как —

  • Microsoft Assembler (MASM)
  • Borland Turbo Assembler (TASM)
  • GNU ассемблер (ГАЗ)

Мы будем использовать ассемблер NASM, так как он —

  • Свободно. Вы можете скачать его из различных веб-источников.
  • Хорошо задокументировано, и вы получите много информации в сети.
  • Может использоваться как в Linux, так и в Windows.

Установка NASM

Если вы выбираете «Инструменты разработки» при установке Linux, вы можете установить NASM вместе с операционной системой Linux, и вам не нужно загружать и устанавливать его отдельно. Чтобы проверить, установлен ли у вас NASM, сделайте следующее:

  • Откройте терминал Linux.

  • Введите whereis nasm и нажмите клавишу ВВОД.

  • Если он уже установлен, появляется строка типа nasm: / usr / bin / nasm . В противном случае вы увидите просто nasm:, тогда вам нужно установить NASM.

Откройте терминал Linux.

Введите whereis nasm и нажмите клавишу ВВОД.

Если он уже установлен, появляется строка типа nasm: / usr / bin / nasm . В противном случае вы увидите просто nasm:, тогда вам нужно установить NASM.

Чтобы установить NASM, сделайте следующее:

  • Проверьте веб-сайт сборщика сети (NASM) для последней версии.

  • Загрузите исходный архив Linux nasm-X.XX.ta.gz , где X.XX — номер версии NASM в архиве.

  • Распакуйте архив в каталог, который создает подкаталог nasm-X. XX nasm-X. XX

  • Перейдите к nasm-X.XX и введите ./configure . Этот сценарий оболочки найдет лучший компилятор C для использования и соответственно настроит Makefiles.

  • Введите make, чтобы создать двоичные файлы nasm и ndisasm.

  • Введите make install, чтобы установить nasm и ndisasm в / usr / local / bin и установить справочные страницы.

Проверьте веб-сайт сборщика сети (NASM) для последней версии.

Загрузите исходный архив Linux nasm-X.XX.ta.gz , где X.XX — номер версии NASM в архиве.

Распакуйте архив в каталог, который создает подкаталог nasm-X. XX nasm-X. XX

Перейдите к nasm-X.XX и введите ./configure . Этот сценарий оболочки найдет лучший компилятор C для использования и соответственно настроит Makefiles.

Введите make, чтобы создать двоичные файлы nasm и ndisasm.

Введите make install, чтобы установить nasm и ndisasm в / usr / local / bin и установить справочные страницы.

Это должно установить NASM в вашей системе. Кроме того, вы можете использовать RPM-дистрибутив для Fedora Linux. Эта версия проще в установке, просто дважды щелкните файл RPM.

Сборка — основной синтаксис

Программу сборки можно разделить на три раздела:

  • Раздел данных ,

  • Раздел BSS , и

  • Текстовый раздел.

Раздел данных ,

Раздел BSS , и

Текстовый раздел.

Раздел данных

Раздел данных используется для объявления инициализированных данных или констант. Эти данные не изменяются во время выполнения. В этом разделе вы можете объявить различные постоянные значения, имена файлов или размер буфера и т. Д.

Синтаксис объявления раздела данных —

section.data

Раздел BSS

Секция bss используется для объявления переменных. Синтаксис объявления раздела bss —

section.bss

Текстовый раздел

Текстовый раздел используется для хранения актуального кода. Этот раздел должен начинаться с объявления global _start , которое сообщает ядру, где начинается выполнение программы.

Синтаксис объявления текстового раздела —

section.text
   global _start
_start:

Комментарии

Комментарий на ассемблере начинается с точки с запятой (;). Он может содержать любой печатный символ, включая пробел. Он может появиться в строке сам по себе, например:

; This program displays a message on screen

или в той же строке вместе с инструкцией, как —

add eax, ebx     ; adds ebx to eax

Заявления на ассемблере

Программы на ассемблере состоят из трех типов утверждений:

  • Исполняемые инструкции или инструкции,
  • Директивы ассемблера или псевдооперации, и
  • Макросы.

Исполняемые инструкции или просто инструкции говорят процессору, что делать. Каждая инструкция состоит из кода операции (код операции). Каждая исполняемая инструкция генерирует одну инструкцию на машинном языке.

Директивы ассемблера или псевдооперации рассказывают ассемблеру о различных аспектах процесса сборки. Они не являются исполняемыми и не генерируют инструкции машинного языка.

Макросы — это в основном механизм подстановки текста.

Синтаксис операторов ассемблера

Операторы языка ассемблера вводятся по одной инструкции в каждой строке. Каждое утверждение имеет следующий формат —

[label]   mnemonic   [operands]   [;comment]

Поля в квадратных скобках являются необязательными. Базовая инструкция состоит из двух частей: первая — это имя инструкции (или мнемоника), которая должна быть выполнена, а вторая — операнды или параметры команды.

Ниже приведены некоторые примеры типичных операторов ассемблера.

INC COUNT        ; Increment the memory variable COUNT

MOV TOTAL, 48    ; Transfer the value 48 in the 
                 ; memory variable TOTAL
					  
ADD AH, BH       ; Add the content of the 
                 ; BH register into the AH register
					  
AND MASK1, 128   ; Perform AND operation on the 
                 ; variable MASK1 and 128
					  
ADD MARKS, 10    ; Add 10 to the variable MARKS
MOV AL, 10       ; Transfer the value 10 to the AL register

Программа Hello World в Ассамблее

Следующий код на ассемблере выводит на экран строку «Hello World» —

Live Demo

section	.text
   global _start     ;must be declared for linker (ld)
	
_start:	            ;tells linker entry point
   mov	edx,len     ;message length
   mov	ecx,msg     ;message to write
   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

section	.data
msg db 'Hello, world!', 0xa  ;string to be printed
len equ $ - msg     ;length of the string

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

Hello, world!

Компиляция и компоновка программы сборки в NASM

Убедитесь, что вы указали путь двоичных файлов nasm и ld в переменной окружения PATH. Теперь выполните следующие шаги для компиляции и компоновки вышеуказанной программы:

  • Введите приведенный выше код в текстовом редакторе и сохраните его как hello.asm.

  • Убедитесь, что вы находитесь в той же директории, где вы сохранили hello.asm .

  • Чтобы собрать программу, введите nasm -f elf hello.asm

  • Если есть какая-либо ошибка, вам будет предложено об этом на этом этапе. В противном случае будет создан объектный файл вашей программы с именем hello.o .

  • Чтобы связать объектный файл и создать исполняемый файл с именем hello, введите ld -m elf_i386 -s -o hello hello.o

  • Запустите программу, набрав ./hello

Введите приведенный выше код в текстовом редакторе и сохраните его как hello.asm.

Убедитесь, что вы находитесь в той же директории, где вы сохранили hello.asm .

Чтобы собрать программу, введите nasm -f elf hello.asm

Если есть какая-либо ошибка, вам будет предложено об этом на этом этапе. В противном случае будет создан объектный файл вашей программы с именем hello.o .

Чтобы связать объектный файл и создать исполняемый файл с именем hello, введите ld -m elf_i386 -s -o hello hello.o

Запустите программу, набрав ./hello

Если вы все сделали правильно, на экране появится надпись «Привет, мир!» на экране.

Сборка — Сегменты памяти

Мы уже обсудили три раздела программы сборки. Эти разделы также представляют различные сегменты памяти.

Интересно, что если вы замените ключевое слово раздела на сегмент, вы получите тот же результат. Попробуйте следующий код —

Live Demo

segment .text	   ;code segment
   global_start    ;must be declared for linker 
	
_start:	           ;tell linker entry point
   mov edx,len	   ;message length
   mov ecx,msg     ;message to write
   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

segment .data      ;data segment
msg	db 'Hello, world!',0xa   ;our dear string
len	equ	$ - msg          ;length of our dear string

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

Hello, world!

Сегменты памяти

Модель сегментированной памяти делит системную память на группы независимых сегментов, на которые ссылаются указатели, расположенные в регистрах сегментов. Каждый сегмент используется для хранения данных определенного типа. Один сегмент используется для хранения кодов команд, другой — для хранения элементов данных, а третий — для программного стека.

В свете вышеизложенного мы можем указать различные сегменты памяти как —

  • Сегмент данных. Он представлен разделом .data и .bss . Раздел .data используется для объявления области памяти, где хранятся элементы данных для программы. Этот раздел не может быть расширен после объявления элементов данных, и он остается статическим во всей программе.

    Раздел .bss также является разделом статической памяти, который содержит буферы для данных, которые будут объявлены позже в программе. Эта буферная память заполнена нулями.

  • Сегмент кода. Он представлен разделом .text . Это определяет область в памяти, в которой хранятся коды команд. Это также фиксированная зона.

  • Стек — этот сегмент содержит значения данных, передаваемые функциям и процедурам в программе.

Сегмент данных. Он представлен разделом .data и .bss . Раздел .data используется для объявления области памяти, где хранятся элементы данных для программы. Этот раздел не может быть расширен после объявления элементов данных, и он остается статическим во всей программе.

Раздел .bss также является разделом статической памяти, который содержит буферы для данных, которые будут объявлены позже в программе. Эта буферная память заполнена нулями.

Сегмент кода. Он представлен разделом .text . Это определяет область в памяти, в которой хранятся коды команд. Это также фиксированная зона.

Стек — этот сегмент содержит значения данных, передаваемые функциям и процедурам в программе.

Сборка — Регистры

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

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

Регистры хранят элементы данных для обработки без необходимости доступа к памяти. Ограниченное количество регистров встроено в чип процессора.

Регистры процессора

В архитектуре IA-32 имеется десять 32-разрядных и шесть 16-разрядных процессорных регистров. Регистры сгруппированы в три категории —

  • Общие регистры,
  • Регистры управления и
  • Сегментные регистры.

Общие регистры далее делятся на следующие группы —

  • Регистры данных,
  • Регистры указателя и
  • Индексные регистры.

Регистры данных

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

  • Как полные 32-битные регистры данных: EAX, EBX, ECX, EDX.

  • Нижние половины 32-битных регистров могут использоваться как четыре 16-битных регистра данных: AX, BX, CX и DX.

  • Нижняя и верхняя половины вышеупомянутых четырех 16-битных регистров могут использоваться как восемь 8-битных регистров данных: AH, AL, BH, BL, CH, CL, DH и DL.

Как полные 32-битные регистры данных: EAX, EBX, ECX, EDX.

Нижние половины 32-битных регистров могут использоваться как четыре 16-битных регистра данных: AX, BX, CX и DX.

Нижняя и верхняя половины вышеупомянутых четырех 16-битных регистров могут использоваться как восемь 8-битных регистров данных: AH, AL, BH, BL, CH, CL, DH и DL.

Регистры данных

Некоторые из этих регистров данных имеют конкретное применение в арифметических операциях.

AX — основной аккумулятор ; он используется во вводе / выводе и большинстве арифметических инструкций. Например, в операции умножения один операнд сохраняется в регистре EAX или AX или AL в соответствии с размером операнда.

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

CX известен как регистр подсчета , так как регистры ECX, CX хранят счетчик циклов в итерационных операциях.

DX известен как регистр данных . Он также используется в операциях ввода / вывода. Он также используется с регистром AX вместе с DX для операций умножения и деления, связанных с большими значениями.

Регистры указателя

Регистры указателя являются 32-разрядными регистрами EIP, ESP и EBP и соответствующими 16-разрядными правыми частями IP, SP и BP. Есть три категории регистров указателей —

  • Указатель инструкций (IP) — 16-битный регистр IP хранит адрес смещения следующей команды, которая должна быть выполнена. IP вместе с регистром CS (как CS: IP) дает полный адрес текущей инструкции в сегменте кода.

  • Указатель стека (SP) — 16-разрядный регистр SP обеспечивает значение смещения в программном стеке. SP в сочетании с регистром SS (SS: SP) относится к текущей позиции данных или адреса в программном стеке.

  • Базовый указатель (BP) — 16-битный регистр BP в основном помогает ссылаться на переменные параметра, передаваемые подпрограмме. Адрес в регистре SS объединяется со смещением в BP, чтобы получить местоположение параметра. BP также можно комбинировать с DI и SI в качестве базового регистра для специальной адресации.

Указатель инструкций (IP) — 16-битный регистр IP хранит адрес смещения следующей команды, которая должна быть выполнена. IP вместе с регистром CS (как CS: IP) дает полный адрес текущей инструкции в сегменте кода.

Указатель стека (SP) — 16-разрядный регистр SP обеспечивает значение смещения в программном стеке. SP в сочетании с регистром SS (SS: SP) относится к текущей позиции данных или адреса в программном стеке.

Базовый указатель (BP) — 16-битный регистр BP в основном помогает ссылаться на переменные параметра, передаваемые подпрограмме. Адрес в регистре SS объединяется со смещением в BP, чтобы получить местоположение параметра. BP также можно комбинировать с DI и SI в качестве базового регистра для специальной адресации.

Регистры указателя

Индексные регистры

32-разрядные индексные регистры ESI и EDI и их 16-разрядные крайние правые части. SI и DI, используются для индексированной адресации и иногда используются для сложения и вычитания. Есть два набора указателей индекса —

  • Исходный индекс (SI) — используется в качестве исходного индекса для строковых операций.

  • Указатель назначения (DI) — используется как указатель назначения для строковых операций.

Исходный индекс (SI) — используется в качестве исходного индекса для строковых операций.

Указатель назначения (DI) — используется как указатель назначения для строковых операций.

Индексные регистры

Регистры управления

Регистр указателя 32-битной инструкции и регистр 32-битных флагов рассматриваются как регистры управления.

Многие инструкции включают сравнения и математические вычисления и изменяют состояние флагов, а некоторые другие условные инструкции проверяют значение этих флагов состояния, чтобы перенести поток управления в другое место.

Общие биты флага:

  • Флаг переполнения (OF) — указывает на переполнение старшего бита (крайнего левого бита) данных после арифметической операции со знаком.

  • Флаг направления (DF) — определяет направление влево или вправо для перемещения или сравнения строковых данных. Когда значение DF равно 0, строковая операция принимает направление слева направо, а когда значение равно 1, строковая операция принимает направление справа налево.

  • Флаг прерывания (IF) — определяет, будут ли игнорироваться или обрабатываться внешние прерывания, такие как ввод с клавиатуры и т. Д. Он отключает внешнее прерывание, когда значение равно 0, и разрешает прерывания, когда установлено значение 1.

  • Trap Flag (TF) — позволяет настроить работу процессора в одношаговом режиме. Программа DEBUG, которую мы использовали, устанавливает флаг прерывания, чтобы мы могли выполнять выполнение одной инструкции за раз.

  • Флаг знака (SF) — показывает знак результата арифметической операции. Этот флаг устанавливается в соответствии со знаком элемента данных после арифметической операции. Знак указывается старшим левым битом. Положительный результат очищает значение SF до 0, а отрицательный результат устанавливает его в 1.

  • Нулевой флаг (ZF) — указывает результат арифметической операции или операции сравнения. Ненулевой результат очищает нулевой флаг до 0, а нулевой результат устанавливает его в 1.

  • Вспомогательный флаг переноса (AF) — содержит перенос с бита 3 на бит 4 после арифметической операции; используется для специализированной арифметики. AF устанавливается, когда 1-байтовая арифметическая операция вызывает перенос из бита 3 в бит 4.

  • Флаг четности (PF) — указывает общее количество 1-битов в результате, полученном в результате арифметической операции. Чётное число 1-бит очищает флаг четности до 0, а нечетное число 1-бит устанавливает флаг четности в 1.

  • Флаг переноса (CF) — содержит перенос 0 или 1 из старшего бита (крайнего слева) после арифметической операции. Он также хранит содержимое последнего бита операции сдвига или поворота .

Флаг переполнения (OF) — указывает на переполнение старшего бита (крайнего левого бита) данных после арифметической операции со знаком.

Флаг направления (DF) — определяет направление влево или вправо для перемещения или сравнения строковых данных. Когда значение DF равно 0, строковая операция принимает направление слева направо, а когда значение равно 1, строковая операция принимает направление справа налево.

Флаг прерывания (IF) — определяет, будут ли игнорироваться или обрабатываться внешние прерывания, такие как ввод с клавиатуры и т. Д. Он отключает внешнее прерывание, когда значение равно 0, и разрешает прерывания, когда установлено значение 1.

Trap Flag (TF) — позволяет настроить работу процессора в одношаговом режиме. Программа DEBUG, которую мы использовали, устанавливает флаг прерывания, чтобы мы могли выполнять выполнение одной инструкции за раз.

Флаг знака (SF) — показывает знак результата арифметической операции. Этот флаг устанавливается в соответствии со знаком элемента данных после арифметической операции. Знак указывается старшим левым битом. Положительный результат очищает значение SF до 0, а отрицательный результат устанавливает его в 1.

Нулевой флаг (ZF) — указывает результат арифметической операции или операции сравнения. Ненулевой результат очищает нулевой флаг до 0, а нулевой результат устанавливает его в 1.

Вспомогательный флаг переноса (AF) — содержит перенос с бита 3 на бит 4 после арифметической операции; используется для специализированной арифметики. AF устанавливается, когда 1-байтовая арифметическая операция вызывает перенос из бита 3 в бит 4.

Флаг четности (PF) — указывает общее количество 1-битов в результате, полученном в результате арифметической операции. Чётное число 1-бит очищает флаг четности до 0, а нечетное число 1-бит устанавливает флаг четности в 1.

Флаг переноса (CF) — содержит перенос 0 или 1 из старшего бита (крайнего слева) после арифметической операции. Он также хранит содержимое последнего бита операции сдвига или поворота .

В следующей таблице указано положение битов флага в 16-битном регистре флагов:

Флаг: О D я T S Z п С
Бит нет: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

Сегментные регистры

Сегменты — это определенные области, определенные в программе для хранения данных, кода и стека. Есть три основных сегмента —

  • Сегмент кода — содержит все инструкции, которые должны быть выполнены. 16-битный регистр сегмента кода или регистр CS хранит начальный адрес сегмента кода.

  • Сегмент данных — содержит данные, константы и рабочие области. 16-битный регистр сегмента данных или регистр DS хранит начальный адрес сегмента данных.

  • Сегмент стека — содержит данные и адреса возврата процедур или подпрограмм. Он реализован в виде структуры данных стека. Регистр сегмента стека или регистр SS хранит начальный адрес стека.

Сегмент кода — содержит все инструкции, которые должны быть выполнены. 16-битный регистр сегмента кода или регистр CS хранит начальный адрес сегмента кода.

Сегмент данных — содержит данные, константы и рабочие области. 16-битный регистр сегмента данных или регистр DS хранит начальный адрес сегмента данных.

Сегмент стека — содержит данные и адреса возврата процедур или подпрограмм. Он реализован в виде структуры данных стека. Регистр сегмента стека или регистр SS хранит начальный адрес стека.

Помимо регистров DS, CS и SS существуют и другие регистры дополнительных сегментов — ES (дополнительный сегмент), FS и GS, которые предоставляют дополнительные сегменты для хранения данных.

При программировании на ассемблере программе необходим доступ к ячейкам памяти. Все области памяти в сегменте относятся к начальному адресу сегмента. Сегмент начинается с адреса, равномерно делимого на 16 или шестнадцатеричного числа 10. Таким образом, крайняя правая шестнадцатеричная цифра во всех таких адресах памяти равна 0, что обычно не сохраняется в регистрах сегментов.

Сегментные регистры хранят начальные адреса сегмента. Чтобы получить точное местоположение данных или инструкции в сегменте, требуется значение смещения (или смещение). Чтобы сослаться на любую ячейку памяти в сегменте, процессор объединяет адрес сегмента в регистре сегмента со значением смещения местоположения.

пример

Посмотрите на следующую простую программу, чтобы понять использование регистров в программировании сборки. Эта программа отображает 9 звезд на экране вместе с простым сообщением —

Live Demo

section	.text
   global _start	 ;must be declared for linker (gcc)
	
_start:	         ;tell linker entry point
   mov	edx,len  ;message length
   mov	ecx,msg  ;message to write
   mov	ebx,1    ;file descriptor (stdout)
   mov	eax,4    ;system call number (sys_write)
   int	0x80     ;call kernel
	
   mov	edx,9    ;message length
   mov	ecx,s2   ;message to write
   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
	
section	.data
msg db 'Displaying 9 stars',0xa ;a message
len equ $ - msg  ;length of message
s2 times 9 db '*'

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

Displaying 9 stars
*********

Сборка — Системные вызовы

Системные вызовы — это API для интерфейса между пространством пользователя и пространством ядра. Мы уже использовали системные вызовы. sys_write и sys_exit для записи на экран и выхода из программы соответственно.

Системные вызовы Linux

Вы можете использовать системные вызовы Linux в ваших ассемблерных программах. Для использования системных вызовов Linux в вашей программе необходимо выполнить следующие шаги:

  • Поместите номер системного вызова в регистр EAX.
  • Сохраните аргументы системного вызова в регистрах EBX, ECX и т. Д.
  • Вызовите соответствующее прерывание (80ч).
  • Результат обычно возвращается в регистр EAX.

Существует шесть регистров, в которых хранятся аргументы используемого системного вызова. Это EBX, ECX, EDX, ESI, EDI и EBP. Эти регистры принимают последовательные аргументы, начиная с регистра EBX. Если существует более шести аргументов, ячейка памяти первого аргумента сохраняется в регистре EBX.

В следующем фрагменте кода показано использование системного вызова sys_exit —

mov	eax,1		; system call number (sys_exit)
int	0x80		; call kernel

В следующем фрагменте кода показано использование системного вызова sys_write —

mov	edx,4		; message length
mov	ecx,msg		; message to write
mov	ebx,1		; file descriptor (stdout)
mov	eax,4		; system call number (sys_write)
int	0x80		; call kernel

Все системные вызовы перечислены в /usr/include/asm/unistd.h вместе с их номерами (значение, которое нужно указать в EAX перед вызовом int 80h).

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

% е название % EBX % ECX % EDX % ESX % еди
1 sys_exit ИНТ
2 sys_fork struct pt_regs
3 sys_read без знака int символ * size_t
4 sys_write без знака int const char * size_t
5 sys_open const char * ИНТ ИНТ
6 sys_close без знака int

пример

Следующий пример читает число с клавиатуры и отображает его на экране —

Live Demo

section .data                           ;Data segment
   userMsg db 'Please enter a number: ' ;Ask the user to enter a number
   lenUserMsg equ $-userMsg             ;The length of the message
   dispMsg db 'You have entered: '
   lenDispMsg equ $-dispMsg                 

section .bss           ;Uninitialized data
   num resb 5
	
section .text          ;Code Segment
   global _start
	
_start:                ;User prompt
   mov eax, 4
   mov ebx, 1
   mov ecx, userMsg
   mov edx, lenUserMsg
   int 80h

   ;Read and store the user input
   mov eax, 3
   mov ebx, 2
   mov ecx, num  
   mov edx, 5          ;5 bytes (numeric, 1 for sign) of that information
   int 80h
	
   ;Output the message 'The entered number is: '
   mov eax, 4
   mov ebx, 1
   mov ecx, dispMsg
   mov edx, lenDispMsg
   int 80h  

   ;Output the number entered
   mov eax, 4
   mov ebx, 1
   mov ecx, num
   mov edx, 5
   int 80h  
    
   ; Exit code
   mov eax, 1
   mov ebx, 0
   int 80h

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

Please enter a number:
1234  
You have entered:1234

Сборка — Режимы адресации

Большинство инструкций на ассемблере требуют обработки операндов. Адрес операнда предоставляет место, где хранятся данные, подлежащие обработке. Некоторые инструкции не требуют операнда, в то время как некоторые другие инструкции могут требовать один, два или три операнда.

Когда инструкции требуется два операнда, первый операнд обычно является пунктом назначения, который содержит данные в регистре или ячейке памяти, а второй операнд является источником. Источник содержит либо данные для доставки (немедленная адресация), либо адрес (в регистре или памяти) данных. Как правило, исходные данные остаются неизменными после операции.

Три основных режима адресации —

  • Регистрация адресации
  • Немедленная адресация
  • Адресация памяти

Регистрация адресации

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

Например,

MOV DX, TAX_RATE   ; Register in first operand
MOV COUNT, CX	   ; Register in second operand
MOV EAX, EBX	   ; Both the operands are in registers

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

Немедленная адресация

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

Например,

BYTE_VALUE  DB  150    ; A byte value is defined
WORD_VALUE  DW  300    ; A word value is defined
ADD  BYTE_VALUE, 65    ; An immediate operand 65 is added
MOV  AX, 45H           ; Immediate constant 45H is transferred to AX

Прямая адресация памяти

Когда операнды указываются в режиме адресации памяти, требуется прямой доступ к основной памяти, обычно к сегменту данных. Этот способ адресации приводит к более медленной обработке данных. Чтобы найти точное местоположение данных в памяти, нам нужен начальный адрес сегмента, который обычно находится в регистре DS, и значение смещения. Это значение смещения также называется действующим адресом .

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

При прямой адресации в памяти один из операндов ссылается на ячейку памяти, а другой операнд ссылается на регистр.

Например,

ADD	BYTE_VALUE, DL	; Adds the register in the memory location
MOV	BX, WORD_VALUE	; Operand from the memory is added to register

Прямая смещение адресации

Этот режим адресации использует арифметические операторы для изменения адреса. Например, посмотрите на следующие определения, которые определяют таблицы данных —

BYTE_TABLE DB  14, 15, 22, 45      ; Tables of bytes
WORD_TABLE DW  134, 345, 564, 123  ; Tables of words

Следующие операции обращаются к данным из таблиц в памяти в регистры —

MOV CL, BYTE_TABLE[2]	; Gets the 3rd element of the BYTE_TABLE
MOV CL, BYTE_TABLE + 2	; Gets the 3rd element of the BYTE_TABLE
MOV CX, WORD_TABLE[3]	; Gets the 4th element of the WORD_TABLE
MOV CX, WORD_TABLE + 3	; Gets the 4th element of the WORD_TABLE

Косвенная адресация памяти

В этом режиме адресации используется способность компьютера « Сегмент: смещение» . Обычно для этой цели используются базовые регистры EBX, EBP (или BX, BP) и регистры индекса (DI, SI), закодированные в квадратных скобках для ссылок на память.

Косвенная адресация обычно используется для переменных, содержащих несколько элементов, таких как массивы. Начальный адрес массива хранится, скажем, в регистре EBX.

В следующем фрагменте кода показано, как получить доступ к различным элементам переменной.

MY_TABLE TIMES 10 DW 0  ; Allocates 10 words (2 bytes) each initialized to 0
MOV EBX, [MY_TABLE]     ; Effective Address of MY_TABLE in EBX
MOV [EBX], 110          ; MY_TABLE[0] = 110
ADD EBX, 2              ; EBX = EBX +2
MOV [EBX], 123          ; MY_TABLE[1] = 123

Инструкция MOV

Мы уже использовали инструкцию MOV, которая используется для перемещения данных из одного пространства хранения в другое. Инструкция MOV принимает два операнда.

Синтаксис

Синтаксис инструкции MOV —

MOV  destination, source

Инструкция MOV может иметь одну из следующих пяти форм —

MOV  register, register
MOV  register, immediate
MOV  memory, immediate
MOV  register, memory
MOV  memory, register

Пожалуйста, обратите внимание, что —

  • Оба операнда в операции MOV должны быть одинакового размера
  • Значение исходного операнда остается неизменным

Инструкция MOV порой вызывает двусмысленность. Например, посмотрите на утверждения —

MOV  EBX, [MY_TABLE]  ; Effective Address of MY_TABLE in EBX
MOV  [EBX], 110	      ; MY_TABLE[0] = 110

Не ясно, хотите ли вы переместить байтовый эквивалент или словесный эквивалент числа 110. В таких случаях целесообразно использовать спецификатор типа .

В следующей таблице приведены некоторые общие спецификаторы типов —

Тип Спецификатор Байт адресован
БАЙТ 1
СЛОВО 2
DWORD 4
QWORD 8
Тбайт 10

пример

Следующая программа иллюстрирует некоторые из концепций, обсужденных выше. Он сохраняет имя «Zara Ali» в разделе данных памяти, затем программно меняет его значение на другое имя «Nuha Ali» и отображает оба имени.

Live Demo

section	.text
   global_start     ;must be declared for linker (ld)
_start:             ;tell linker entry point
	
   ;writing the name 'Zara Ali'
   mov	edx,9       ;message length
   mov	ecx, name   ;message to write
   mov	ebx,1       ;file descriptor (stdout)
   mov	eax,4       ;system call number (sys_write)
   int	0x80        ;call kernel
	
   mov	[name],  dword 'Nuha'    ; Changed the name to Nuha Ali
	
   ;writing the name 'Nuha Ali'
   mov	edx,8       ;message length
   mov	ecx,name    ;message to write
   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

section	.data
name db 'Zara Ali '

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

Zara Ali Nuha Ali

Сборка — Переменные

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

Выделение дискового пространства для инициализированных данных

Синтаксис для оператора распределения памяти для инициализированных данных:

[variable-name]    define-directive    initial-value   [,initial-value]...

Где имя переменной — это идентификатор для каждого пространства хранения. Ассемблер связывает значение смещения для каждого имени переменной, определенного в сегменте данных.

Существует пять основных форм директивы определения:

директива Цель Место для хранения
база данных Определить байт выделяет 1 байт
DW Определить слово выделяет 2 байта
DD Определить двойное слово выделяет 4 байта
DQ Определить Quadword выделяет 8 байтов
DT Определить десять байтов выделяет 10 байтов

Ниже приведены некоторые примеры использования директив define.

choice		DB	'y'
number		DW	12345
neg_number	DW	-12345
big_number	DQ	123456789
real_number1	DD	1.234
real_number2	DQ	123.456

Пожалуйста, обратите внимание, что —

  • Каждый байт символа хранится как его значение ASCII в шестнадцатеричном формате.

  • Каждое десятичное значение автоматически преобразуется в его 16-разрядный двоичный эквивалент и сохраняется в виде шестнадцатеричного числа.

  • Процессор использует байтовый порядок байтов.

  • Отрицательные числа преобразуются в его представление дополнения 2.

  • Короткие и длинные числа с плавающей запятой представлены с использованием 32 или 64 бит соответственно.

Каждый байт символа хранится как его значение ASCII в шестнадцатеричном формате.

Каждое десятичное значение автоматически преобразуется в его 16-разрядный двоичный эквивалент и сохраняется в виде шестнадцатеричного числа.

Процессор использует байтовый порядок байтов.

Отрицательные числа преобразуются в его представление дополнения 2.

Короткие и длинные числа с плавающей запятой представлены с использованием 32 или 64 бит соответственно.

Следующая программа показывает использование директивы define —

Live Demo

section .text
   global _start          ;must be declared for linker (gcc)
	
_start:                   ;tell linker entry point
   mov	edx,1		  ;message length
   mov	ecx,choice        ;message to write
   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

section .data
choice DB 'y'

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

y

Выделение дискового пространства для неинициализированных данных

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

Существует пять основных форм директивы о запасах —

директива Цель
RESB Зарезервировать байт
RESW Зарезервировать слово
RESD Зарезервировать двойное слово
RESQ Зарезервировать Quadword
ОСТАЛЬНОЕ Зарезервировать десять байт

Несколько определений

Вы можете иметь несколько операторов определения данных в программе. Например —

choice	  DB 	'Y' 		 ;ASCII of y = 79H
number1	  DW 	12345 	 ;12345D = 3039H
number2    DD  12345679  ;123456789D = 75BCD15H

Ассемблер выделяет непрерывную память для нескольких определений переменных.

Несколько инициализаций

Директива TIMES позволяет несколько инициализаций к одному и тому же значению. Например, массив с именем marks размера 9 может быть определен и инициализирован в ноль с помощью следующего оператора —

marks  TIMES  9  DW  0

Директива TIMES полезна при определении массивов и таблиц. Следующая программа отображает 9 звездочек на экране —

Live Demo

section	.text
   global _start        ;must be declared for linker (ld)
	
_start:                 ;tell linker entry point
   mov	edx,9		;message length
   mov	ecx, stars	;message to write
   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

section	.data
stars   times 9 db '*'

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

*********

Сборка — Константы

NASM предоставляет несколько директив, определяющих константы. Мы уже использовали директиву EQU в предыдущих главах. Мы будем особенно обсуждать три директивы —

  • EQU
  • % правопреемник
  • % определяют

Директива EQU

Директива EQU используется для определения констант. Синтаксис директивы EQU следующий:

CONSTANT_NAME EQU expression

Например,

TOTAL_STUDENTS equ 50

Затем вы можете использовать это постоянное значение в вашем коде, например:

mov  ecx,  TOTAL_STUDENTS 
cmp  eax,  TOTAL_STUDENTS

Операндом оператора EQU может быть выражение —

LENGTH equ 20
WIDTH  equ 10
AREA   equ length * width

Выше сегмент кода будет определять AREA как 200.

пример

Следующий пример иллюстрирует использование директивы EQU —

Live Demo

SYS_EXIT  equ 1
SYS_WRITE equ 4
STDIN     equ 0
STDOUT    equ 1
section	 .text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point
   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg1         
   mov edx, len1 
   int 0x80                
	
   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg2         
   mov edx, len2 
   int 0x80 
	
   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg3         
   mov edx, len3 
   int 0x80
   
   mov eax,SYS_EXIT    ;system call number (sys_exit)
   int 0x80            ;call kernel

section	 .data
msg1 db	'Hello, programmers!',0xA,0xD 	
len1 equ $ - msg1			

msg2 db 'Welcome to the world of,', 0xA,0xD 
len2 equ $ - msg2 

msg3 db 'Linux assembly programming! '
len3 equ $- msg3

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

Hello, programmers!
Welcome to the world of,
Linux assembly programming!

Директива% assign

Директива % assign может использоваться для определения числовых констант, таких как директива EQU. Эта директива допускает переопределение. Например, вы можете определить постоянную ВСЕГО как

%assign TOTAL 10

Позже в коде вы можете переопределить его как —

%assign  TOTAL  20

Эта директива чувствительна к регистру.

% Определяет директиву

Директива % define позволяет определять как числовые, так и строковые константы. Эта директива похожа на #define в C. Например, вы можете определить постоянную PTR как —

%define PTR [EBP+4]

Приведенный выше код заменяет PTR на [EBP + 4].

Эта директива также допускает переопределение и учитывает регистр.

Сборка — Арифметические инструкции

Инструкция МКП

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

Синтаксис

Инструкция INC имеет следующий синтаксис —

INC destination

Назначением операнда может быть 8-битный, 16-битный или 32-битный операнд.

пример

INC EBX	     ; Increments 32-bit register
INC DL       ; Increments 8-bit register
INC [count]  ; Increments the count variable

Инструкция DEC

Инструкция DEC используется для уменьшения операнда на единицу. Он работает с одним операндом, который может находиться либо в регистре, либо в памяти.

Синтаксис

Инструкция DEC имеет следующий синтаксис —

DEC destination

Назначением операнда может быть 8-битный, 16-битный или 32-битный операнд.

пример

segment .data
   count dw  0
   value db  15
	
segment .text
   inc [count]
   dec [value]
	
   mov ebx, count
   inc word [ebx]
	
   mov esi, value
   dec byte [esi]

ADD и SUB инструкции

Команды ADD и SUB используются для выполнения простого сложения / вычитания двоичных данных в байтах, размерах слова и двойного слова, то есть для сложения или вычитания 8-битных, 16-битных или 32-битных операндов соответственно.

Синтаксис

Инструкции ADD и SUB имеют следующий синтаксис:

ADD/SUB	destination, source

Инструкция ADD / SUB может выполняться между:

  • Зарегистрируйтесь, чтобы зарегистрироваться
  • Память для регистрации
  • Зарегистрироваться в память
  • Зарегистрироваться на постоянные данные
  • Память на постоянные данные

Однако, как и другие инструкции, операции с памятью в память невозможны с использованием инструкций ADD / SUB. Операция ADD или SUB устанавливает или очищает переполнение и переносит флаги.

пример

В следующем примере пользователь запросит две цифры, сохранит их в регистре EAX и EBX, добавит значения, сохранит результат в ячейке памяти « res » и, наконец, отобразит результат.

Live Demo

SYS_EXIT  equ 1
SYS_READ  equ 3
SYS_WRITE equ 4
STDIN     equ 0
STDOUT    equ 1

segment .data 

   msg1 db "Enter a digit ", 0xA,0xD 
   len1 equ $- msg1 

   msg2 db "Please enter a second digit", 0xA,0xD 
   len2 equ $- msg2 

   msg3 db "The sum is: "
   len3 equ $- msg3

segment .bss

   num1 resb 2 
   num2 resb 2 
   res resb 1    

section	.text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point
   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg1         
   mov edx, len1 
   int 0x80                

   mov eax, SYS_READ 
   mov ebx, STDIN  
   mov ecx, num1 
   mov edx, 2
   int 0x80            

   mov eax, SYS_WRITE        
   mov ebx, STDOUT         
   mov ecx, msg2          
   mov edx, len2         
   int 0x80

   mov eax, SYS_READ  
   mov ebx, STDIN  
   mov ecx, num2 
   mov edx, 2
   int 0x80        

   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg3          
   mov edx, len3         
   int 0x80

   ; moving the first number to eax register and second number to ebx
   ; and subtracting ascii '0' to convert it into a decimal number
	
   mov eax, [num1]
   sub eax, '0'
	
   mov ebx, [num2]
   sub ebx, '0'

   ; add eax and ebx
   add eax, ebx
   ; add '0' to to convert the sum from decimal to ASCII
   add eax, '0'

   ; storing the sum in memory location res
   mov [res], eax

   ; print the sum 
   mov eax, SYS_WRITE        
   mov ebx, STDOUT
   mov ecx, res         
   mov edx, 1        
   int 0x80

exit:    
   
   mov eax, SYS_EXIT   
   xor ebx, ebx 
   int 0x80

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

Enter a digit:
3
Please enter a second digit:
4
The sum is:
7

Программа с жестко закодированными переменными —

Live Demo

section	.text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point
   mov	eax,'3'
   sub     eax, '0'
	
   mov 	ebx, '4'
   sub     ebx, '0'
   add 	eax, ebx
   add	eax, '0'
	
   mov 	[sum], eax
   mov	ecx,msg	
   mov	edx, len
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	ecx,sum
   mov	edx, 1
   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
	
section .data
   msg db "The sum is:", 0xA,0xD 
   len equ $ - msg   
   segment .bss
   sum resb 1

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

The sum is:
7

Инструкция MUL / IMUL

Есть две инструкции для умножения двоичных данных. Инструкция MUL (Multiply) обрабатывает неподписанные данные, а IMUL (Integer Multiply) обрабатывает подписанные данные. Обе инструкции влияют на флаг переноса и переполнения.

Синтаксис

Синтаксис для инструкций MUL / IMUL следующий:

MUL/IMUL multiplier

Мультипликатор в обоих случаях будет в аккумуляторе, в зависимости от размера мультипликатора и умножителя, и сгенерированный продукт также сохраняется в двух регистрах в зависимости от размера операндов. Следующий раздел объясняет инструкции MUL в трех разных случаях:

Sr.No. Сценарии
1

Когда два байта умножены —

Умножитель находится в регистре AL, а множитель — это байт в памяти или в другом регистре. Продукт находится в AX. Старшие 8 битов продукта хранятся в AH, а младшие 8 битов хранятся в AL.

Arithmetic1

2

Когда умножаются два значения в одно слово

Умножитель должен быть в регистре AX, а множитель — это слово в памяти или другой регистр. Например, для такой инструкции, как MUL DX, вы должны сохранить множитель в DX и умножить в AX.

В результате получается двойное слово, для которого понадобятся два регистра. Часть высшего порядка (крайняя слева) сохраняется в DX, а часть нижнего порядка (крайняя справа) сохраняется в AX.

Arithmetic2

3

Когда два значения двойного слова умножаются —

Когда умножаются два значения двойного слова, множитель должен быть в EAX, а множитель — это значение двойного слова, хранящееся в памяти или в другом регистре. Сгенерированный продукт сохраняется в регистрах EDX: EAX, т. Е. 32-разрядные старшие разряды сохраняются в регистре EDX, а 32-разрядные старшие разряды хранятся в регистре EAX.

Arithmetic3

Когда два байта умножены —

Умножитель находится в регистре AL, а множитель — это байт в памяти или в другом регистре. Продукт находится в AX. Старшие 8 битов продукта хранятся в AH, а младшие 8 битов хранятся в AL.

Arithmetic1

Когда умножаются два значения в одно слово

Умножитель должен быть в регистре AX, а множитель — это слово в памяти или другой регистр. Например, для такой инструкции, как MUL DX, вы должны сохранить множитель в DX и умножить в AX.

В результате получается двойное слово, для которого понадобятся два регистра. Часть высшего порядка (крайняя слева) сохраняется в DX, а часть нижнего порядка (крайняя справа) сохраняется в AX.

Arithmetic2

Когда два значения двойного слова умножаются —

Когда умножаются два значения двойного слова, множитель должен быть в EAX, а множитель — это значение двойного слова, хранящееся в памяти или в другом регистре. Сгенерированный продукт сохраняется в регистрах EDX: EAX, т. Е. 32-разрядные старшие разряды сохраняются в регистре EDX, а 32-разрядные старшие разряды хранятся в регистре EAX.

Arithmetic3

пример

MOV AL, 10
MOV DL, 25
MUL DL
...
MOV DL, 0FFH	; DL= -1
MOV AL, 0BEH	; AL = -66
IMUL DL

пример

В следующем примере 3 умножается на 2 и отображается результат:

Live Demo

section	.text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point

   mov	al,'3'
   sub     al, '0'
	
   mov 	bl, '2'
   sub     bl, '0'
   mul 	bl
   add	al, '0'
	
   mov 	[res], al
   mov	ecx,msg	
   mov	edx, len
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	ecx,res
   mov	edx, 1
   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

section .data
msg db "The result is:", 0xA,0xD 
len equ $- msg   
segment .bss
res resb 1

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

The result is:
6

Инструкции DIV / IDIV

Операция деления генерирует два элемента — частное и остаток . В случае умножения переполнение не происходит, потому что регистры двойной длины используются для хранения продукта. Однако в случае деления может произойти переполнение. Процессор генерирует прерывание, если происходит переполнение.

Инструкция DIV (Divide) используется для данных без знака, а IDIV (Integer Divide) используется для данных со знаком.

Синтаксис

Формат для инструкции DIV / IDIV —

DIV/IDIV	divisor

Дивиденд находится в аккумуляторе. Обе инструкции могут работать с 8-битными, 16-битными или 32-битными операндами. Операция влияет на все шесть флагов состояния. Следующий раздел объясняет три случая деления с различным размером операнда —

Sr.No. Сценарии
1

Когда делитель равен 1 байту —

Предполагается, что дивиденд находится в регистре AX (16 бит). После деления частное переходит в регистр AL, а остальное — в регистр AH.

Arithmetic4

2

Когда делителем является 1 слово —

Предполагается, что дивиденды имеют длину 32 бита и в регистрах DX: AX. Старшие 16 битов находятся в DX, а младшие 16 битов — в AX. После деления 16-битное отношение попадает в регистр AX, а 16-битное значение попадает в регистр DX.

Arithmetic5

3

Когда делитель двойное слово —

Предполагается, что размер дивиденда составляет 64 бита и в регистрах EDX: EAX. Старшие 32 бита находятся в EDX, а младшие 32 бита находятся в EAX. После деления 32-битное отношение попадает в регистр EAX, а 32-битное значение попадает в регистр EDX.

Arithmetic6

Когда делитель равен 1 байту —

Предполагается, что дивиденд находится в регистре AX (16 бит). После деления частное переходит в регистр AL, а остальное — в регистр AH.

Arithmetic4

Когда делителем является 1 слово —

Предполагается, что дивиденды имеют длину 32 бита и в регистрах DX: AX. Старшие 16 битов находятся в DX, а младшие 16 битов — в AX. После деления 16-битное отношение попадает в регистр AX, а 16-битное значение попадает в регистр DX.

Arithmetic5

Когда делитель двойное слово —

Предполагается, что размер дивиденда составляет 64 бита и в регистрах EDX: EAX. Старшие 32 бита находятся в EDX, а младшие 32 бита находятся в EAX. После деления 32-битное отношение попадает в регистр EAX, а 32-битное значение попадает в регистр EDX.

Arithmetic6

пример

В следующем примере делится 8 на 2. Дивиденд 8 сохраняется в 16-битном регистре AX, а делитель 2 сохраняется в 8-битном регистре BL .

Live Demo

section	.text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point
   mov	ax,'8'
   sub     ax, '0'
	
   mov 	bl, '2'
   sub     bl, '0'
   div 	bl
   add	ax, '0'
	
   mov 	[res], ax
   mov	ecx,msg	
   mov	edx, len
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	ecx,res
   mov	edx, 1
   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
	
section .data
msg db "The result is:", 0xA,0xD 
len equ $- msg   
segment .bss
res resb 1

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

The result is:
4

Сборка — логические инструкции

Набор команд процессора содержит инструкции логики AND, OR, XOR, TEST и NOT, которые проверяют, устанавливают и очищают биты в соответствии с потребностями программы.

Формат для этих инструкций —

Sr.No. инструкция Формат
1 А ТАКЖЕ И операнд1, операнд2
2 ИЛИ ЖЕ ИЛИ операнд1, операнд2
3 XOR XOR операнд1, операнд2
4 ТЕСТОВОЕ ЗАДАНИЕ ТЕСТ операнд1, операнд2
5 НЕ НЕ операнд1

Первый операнд во всех случаях может быть либо в регистре, либо в памяти. Второй операнд может быть либо в регистре / памяти, либо в непосредственном (постоянном) значении. Однако операции с памятью в память невозможны. Эти инструкции сравнивают или сопоставляют биты операндов и устанавливают флаги CF, OF, PF, SF и ZF.

Инструкция И

Инструкция AND используется для поддержки логических выражений путем выполнения побитовой операции AND. Побитовая операция И возвращает 1, если совпадающие биты обоих операндов равны 1, в противном случае она возвращает 0. Например, —

             Operand1: 	0101
             Operand2: 	0011
----------------------------
After AND -> Operand1:	0001

Операция AND может использоваться для очистки одного или нескольких битов. Например, допустим, регистр BL содержит 0011 1010. Если вам нужно очистить старшие биты до нуля, вы И это с 0FH.

AND	BL,   0FH   ; This sets BL to 0000 1010

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

Предполагая, что номер находится в регистре AL, мы можем написать —

AND	AL, 01H     ; ANDing with 0000 0001
JZ    EVEN_NUMBER

Следующая программа иллюстрирует это —

пример

Live Demo

section .text
   global _start            ;must be declared for using gcc
	
_start:                     ;tell linker entry point
   mov   ax,   8h           ;getting 8 in the ax 
   and   ax, 1              ;and ax with 1
   jz    evnn
   mov   eax, 4             ;system call number (sys_write)
   mov   ebx, 1             ;file descriptor (stdout)
   mov   ecx, odd_msg       ;message to write
   mov   edx, len2          ;length of message
   int   0x80               ;call kernel
   jmp   outprog

evnn:   
  
   mov   ah,  09h
   mov   eax, 4             ;system call number (sys_write)
   mov   ebx, 1             ;file descriptor (stdout)
   mov   ecx, even_msg      ;message to write
   mov   edx, len1          ;length of message
   int   0x80               ;call kernel

outprog:

   mov   eax,1              ;system call number (sys_exit)
   int   0x80               ;call kernel

section   .data
even_msg  db  'Even Number!' ;message showing even number
len1  equ  $ - even_msg 
   
odd_msg db  'Odd Number!'    ;message showing odd number
len2  equ  $ - odd_msg

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

Even Number!

Измените значение в регистре топоров с нечетной цифрой, например:

mov  ax, 9h                  ; getting 9 in the ax

Программа будет отображать:

Odd Number!

Точно так же очистить весь регистр вы можете И с 00H.

Инструкция OR

Инструкция OR используется для поддержки логического выражения путем выполнения побитовой операции OR. Оператор побитового ИЛИ возвращает 1, если совпадающие биты из одного или обоих операндов равны единице. Возвращает 0, если оба бита равны нулю.

Например,

             Operand1:     0101
             Operand2:     0011
----------------------------
After OR -> Operand1:    0111

Операция ИЛИ может использоваться для установки одного или нескольких битов. Например, предположим, что регистр AL содержит 0011 1010, вам нужно установить четыре младших бита, вы можете ИЛИ со значением 0000 1111, т.е. FH.

OR BL, 0FH                   ; This sets BL to  0011 1111

пример

В следующем примере демонстрируется инструкция OR. Давайте сохраним значения 5 и 3 в регистрах AL и BL, соответственно, затем инструкцию,

OR AL, BL

следует хранить 7 в регистре AL —

Live Demo

section .text
   global _start            ;must be declared for using gcc
	
_start:                     ;tell linker entry point
   mov    al, 5             ;getting 5 in the al
   mov    bl, 3             ;getting 3 in the bl
   or     al, bl            ;or al and bl registers, result should be 7
   add    al, byte '0'      ;converting decimal to ascii
	
   mov    [result],  al
   mov    eax, 4
   mov    ebx, 1
   mov    ecx, result
   mov    edx, 1 
   int    0x80
    
outprog:
   mov    eax,1             ;system call number (sys_exit)
   int    0x80              ;call kernel
	
section    .bss
result resb 1

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

7

Инструкция XOR

Инструкция XOR реализует побитовую операцию XOR. Операция XOR устанавливает результирующий бит в 1, если и только если биты из операндов отличаются. Если биты из операндов одинаковы (оба 0 или оба 1), результирующий бит сбрасывается в 0.

Например,

             Operand1:     0101
             Operand2:     0011
----------------------------
After XOR -> Operand1:    0110

XORing операнда с собой изменяет операнд на 0 . Это используется для очистки регистра.

XOR     EAX, EAX

ТЕСТ Инструкция

Инструкция TEST работает так же, как и операция AND, но в отличие от инструкции AND она не меняет первый операнд. Таким образом, если нам нужно проверить, является ли число в регистре четным или нечетным, мы также можем сделать это, используя инструкцию TEST, не меняя исходного числа.

TEST    AL, 01H
JZ      EVEN_NUMBER

НЕ инструкция

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

Например,

             Operand1:    0101 0011
After NOT -> Operand1:    1010 1100

Сборка — Условия

Условное выполнение на ассемблере выполняется несколькими инструкциями зацикливания и ветвления. Эти инструкции могут изменить поток управления в программе. Условное исполнение наблюдается в двух сценариях —

Sr.No. Условные Инструкции
1

Безусловный прыжок

Это выполняется инструкцией JMP. Условное выполнение часто включает передачу управления на адрес инструкции, которая не следует за выполняемой в настоящее время инструкцией. Передача управления может быть прямой, чтобы выполнить новый набор инструкций, или обратной, чтобы повторно выполнить те же самые шаги.

2

Условный переход

Это выполняется с помощью набора инструкций перехода <условие> в зависимости от условия. Условные инструкции передают управление, прерывая последовательный поток, и делают это, изменяя значение смещения в IP.

Безусловный прыжок

Это выполняется инструкцией JMP. Условное выполнение часто включает передачу управления на адрес инструкции, которая не следует за выполняемой в настоящее время инструкцией. Передача управления может быть прямой, чтобы выполнить новый набор инструкций, или обратной, чтобы повторно выполнить те же самые шаги.

Условный переход

Это выполняется с помощью набора инструкций перехода <условие> в зависимости от условия. Условные инструкции передают управление, прерывая последовательный поток, и делают это, изменяя значение смещения в IP.

Давайте обсудим инструкцию CMP, прежде чем обсуждать условные инструкции.

Инструкция CMP

Инструкция CMP сравнивает два операнда. Обычно используется в условном исполнении. Эта инструкция в основном вычитает один операнд из другого для сравнения, равны ли операнды или нет. Он не мешает операндам назначения или источника. Он используется вместе с инструкцией условного перехода для принятия решения.

Синтаксис

CMP destination, source

CMP сравнивает два числовых поля данных. Операнд-адресат может быть либо в регистре, либо в памяти. Исходным операндом могут быть постоянные (непосредственные) данные, регистр или память.

пример

CMP DX,	00  ; Compare the DX value with zero
JE  L7      ; If yes, then jump to label L7
.
.
L7: ...  

CMP часто используется для сравнения того, достигло ли значение счетчика количества раз, которое цикл должен быть выполнен. Рассмотрим следующее типичное условие —

INC	EDX
CMP	EDX, 10	; Compares whether the counter has reached 10
JLE	LP1     ; If it is less than or equal to 10, then jump to LP1

Безусловный прыжок

Как упоминалось ранее, это выполняется инструкцией JMP. Условное выполнение часто включает передачу управления на адрес инструкции, которая не следует за выполняемой в настоящее время инструкцией. Передача управления может быть прямой, чтобы выполнить новый набор инструкций, или обратной, чтобы повторно выполнить те же самые шаги.

Синтаксис

Инструкция JMP предоставляет имя метки, куда поток управления передается немедленно. Синтаксис инструкции JMP:

JMP	label

пример

Следующий фрагмент кода иллюстрирует инструкцию JMP —

MOV  AX, 00    ; Initializing AX to 0
MOV  BX, 00    ; Initializing BX to 0
MOV  CX, 01    ; Initializing CX to 1
L20:
ADD  AX, 01    ; Increment AX
ADD  BX, AX    ; Add AX to BX
SHL  CX, 1     ; shift left CX, this in turn doubles the CX value
JMP  L20       ; repeats the statements

Условный переход

Если в условном переходе выполняется какое-либо заданное условие, поток управления переносится в целевую инструкцию. Существует множество инструкций условного перехода в зависимости от состояния и данных.

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

инструкция Описание Флаги проверены
JE / JZ Прыжок равный или прыжок ноль ZF
JNE / JNZ Прыжок не равен или Прыжок не ноль ZF
JG / JNLE Прыгай больше или прыгай не меньше / равно OF, SF, ZF
JGE / JNL Прыгай больше / равно или прыгай не меньше OF, SF
JL / JNGE Прыгай меньше или прыгай не больше / равно OF, SF
JLE / JNG Прыгай меньше / равно или прыгай не больше OF, SF, ZF

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

инструкция Описание Флаги проверены
JE / JZ Прыжок равный или прыжок ноль ZF
JNE / JNZ Прыжок не равен или Прыжок не ноль ZF
JA / JNBE Прыгать выше или прыгать не ниже / равно CF, ZF
JAE / JNB Прыгай выше / равно или прыгай не ниже CF
JB / JNAE Прыжок ниже или Прыжок не выше / равно CF
JBE / ЮНА Прыжок ниже / равно или Прыжок не выше AF, CF

Следующие инструкции условного перехода имеют специальное использование и проверяют значение флагов:

инструкция Описание Флаги проверены
JXCZ Прыгать, если CX равен нулю никто
JC Прыгай, если несешь CF
JNC Прыгай, если не неси CF
ДЖО Прыжок, если переполнен О
JNO Прыжок, если нет переполнения О
В JP / JPE Прыжок Паритет или Прыжок Паритет Даже PF
JNP / МСС Прыжок без четности или нечетный четность PF
JS Знак прыжка (отрицательное значение) SF
JNS Прыжок без знака (положительное значение) SF

Синтаксис для набора инструкций J <условие> —

Пример,

CMP	AL, BL
JE	EQUAL
CMP	AL, BH
JE	EQUAL
CMP	AL, CL
JE	EQUAL
NON_EQUAL: ...
EQUAL: ...

пример

Следующая программа отображает наибольшую из трех переменных. Переменные являются двузначными переменными. Три переменные num1, num2 и num3 имеют значения 47, 22 и 31 соответственно —

Live Demo

section	.text
   global _start         ;must be declared for using gcc

_start:	                 ;tell linker entry point
   mov   ecx, [num1]
   cmp   ecx, [num2]
   jg    check_third_num
   mov   ecx, [num2]
   
	check_third_num:

   cmp   ecx, [num3]
   jg    _exit
   mov   ecx, [num3]
   
	_exit:
   
   mov   [largest], ecx
   mov   ecx,msg
   mov   edx, len
   mov   ebx,1	;file descriptor (stdout)
   mov   eax,4	;system call number (sys_write)
   int   0x80	;call kernel
	
   mov   ecx,largest
   mov   edx, 2
   mov   ebx,1	;file descriptor (stdout)
   mov   eax,4	;system call number (sys_write)
   int   0x80	;call kernel
    
   mov   eax, 1
   int   80h

section	.data
   
   msg db "The largest digit is: ", 0xA,0xD 
   len equ $- msg 
   num1 dd '47'
   num2 dd '22'
   num3 dd '31'

segment .bss
   largest resb 2  

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

The largest digit is: 
47

Сборка — Петли

Инструкция JMP может использоваться для реализации циклов. Например, следующий фрагмент кода может использоваться для выполнения тела цикла 10 раз.

MOV	CL, 10
L1:
<LOOP-BODY>
DEC	CL
JNZ	L1

Набор инструкций процессора, однако, включает в себя группу инструкций цикла для реализации итерации. Основная инструкция LOOP имеет следующий синтаксис —

LOOP 	label

Где labelметка цели, которая идентифицирует целевую инструкцию, как в инструкциях перехода. Инструкция LOOP предполагает, что регистр ECX содержит количество циклов . Когда инструкция цикла выполняется, регистр ECX уменьшается, и управление переходит к целевой метке, пока значение регистра ECX, т. Е. Счетчик не достигнет нуля.

Приведенный выше фрагмент кода может быть записан как —

mov ECX,10
l1:
<loop body>
loop l1

пример

Следующая программа печатает цифры от 1 до 9 на экране —

Live Demo

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   mov ecx,10
   mov eax, '1'
	
l1:
   mov [num], eax
   mov eax, 4
   mov ebx, 1
   push ecx
	
   mov ecx, num        
   mov edx, 1        
   int 0x80
	
   mov eax, [num]
   sub eax, '0'
   inc eax
   add eax, '0'
   pop ecx
   loop l1
	
   mov eax,1             ;system call number (sys_exit)
   int 0x80              ;call kernel
section	.bss
num resb 1

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

123456789:

Сборка — Номера

Числовые данные обычно представлены в двоичной системе. Арифметические инструкции работают с двоичными данными. Когда числа отображаются на экране или вводятся с клавиатуры, они имеют форму ASCII.

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

Live Demo

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   mov	eax,'3'
   sub     eax, '0'
	
   mov 	ebx, '4'
   sub     ebx, '0'
   add 	eax, ebx
   add	eax, '0'
	
   mov 	[sum], eax
   mov	ecx,msg	
   mov	edx, len
   mov	ebx,1	         ;file descriptor (stdout)
   mov	eax,4	         ;system call number (sys_write)
   int	0x80	         ;call kernel
	
   mov	ecx,sum
   mov	edx, 1
   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
	
section .data
msg db "The sum is:", 0xA,0xD 
len equ $ - msg   
segment .bss
sum resb 1

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

The sum is:
7

Такие преобразования, однако, имеют накладные расходы, и программирование на ассемблере позволяет более эффективно обрабатывать числа в двоичной форме. Десятичные числа могут быть представлены в двух формах —

  • ASCII форма
  • BCD или двоично-десятичная форма

ASCII Представительство

В представлении ASCII десятичные числа хранятся в виде строки символов ASCII. Например, десятичное значение 1234 сохраняется как —

31	32	33	34H

Где 31H — значение ASCII для 1, 32H — значение ASCII для 2 и т. Д. Есть четыре инструкции для обработки чисел в представлении ASCII —

  • AAA — ASCII настроить после добавления

  • AAS — ASCII отрегулировать после вычитания

  • AAM — ASCII настроить после умножения

  • AAD — ASCII настроить перед делением

AAA — ASCII настроить после добавления

AAS — ASCII отрегулировать после вычитания

AAM — ASCII настроить после умножения

AAD — ASCII настроить перед делением

Эти инструкции не принимают никаких операндов и предполагают, что требуемый операнд находится в регистре AL.

В следующем примере инструкция AAS используется для демонстрации концепции:

Live Demo

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   sub     ah, ah
   mov     al, '9'
   sub     al, '3'
   aas
   or      al, 30h
   mov     [res], ax
	
   mov	edx,len	        ;message length
   mov	ecx,msg	        ;message to write
   mov	ebx,1	        ;file descriptor (stdout)
   mov	eax,4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	edx,1	        ;message length
   mov	ecx,res	        ;message to write
   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
	
section	.data
msg db 'The Result is:',0xa	
len equ $ - msg			
section .bss
res resb 1  

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

The Result is:
6

BCD Представительство

Существует два типа представления BCD —

  • Распакованное BCD представление
  • Упакованное представление BCD

В неупакованном представлении BCD каждый байт хранит двоичный эквивалент десятичной цифры. Например, число 1234 хранится как —

01	02	03	04H

Есть две инструкции для обработки этих чисел —

  • AAM — ASCII настроить после умножения

  • AAD — ASCII настроить перед делением

AAM — ASCII настроить после умножения

AAD — ASCII настроить перед делением

Четыре инструкции настройки ASCII, AAA, AAS, AAM и AAD, также могут использоваться с неупакованным представлением BCD. В упакованном представлении BCD каждая цифра сохраняется с использованием четырех битов. Две десятичные цифры упакованы в байт. Например, число 1234 хранится как —

12	34H

Есть две инструкции для обработки этих чисел —

  • DAA — десятичная корректировка после добавления

  • DAS — десятичное значение после вычитания

DAA — десятичная корректировка после добавления

DAS — десятичное значение после вычитания

В упакованном представлении BCD отсутствует поддержка умножения и деления.

пример

Следующая программа складывает два 5-значных десятичных числа и отображает сумму. Он использует вышеуказанные понятия —

Live Demo

section	.text
   global _start        ;must be declared for using gcc

_start:	                ;tell linker entry point

   mov     esi, 4       ;pointing to the rightmost digit
   mov     ecx, 5       ;num of digits
   clc
add_loop:  
   mov 	al, [num1 + esi]
   adc 	al, [num2 + esi]
   aaa
   pushf
   or 	al, 30h
   popf
	
   mov	[sum + esi], al
   dec	esi
   loop	add_loop
	
   mov	edx,len	        ;message length
   mov	ecx,msg	        ;message to write
   mov	ebx,1	        ;file descriptor (stdout)
   mov	eax,4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	edx,5	        ;message length
   mov	ecx,sum	        ;message to write
   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

section	.data
msg db 'The Sum is:',0xa	
len equ $ - msg			
num1 db '12345'
num2 db '23456'
sum db '     '

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

The Sum is:
35801

Сборка — Струны

Мы уже использовали строки переменной длины в наших предыдущих примерах. Строки переменной длины могут содержать столько символов, сколько необходимо. Как правило, мы указываем длину строки одним из двух способов —

  • Явно хранит длину строки
  • Использование часового персонажа

Мы можем хранить длину строки явно, используя символ счетчика $ location, который представляет текущее значение счетчика местоположений. В следующем примере —

msg  db  'Hello, world!',0xa ;our dear string
len  equ  $ - msg            ;length of our dear string

$ указывает на байт после последнего символа строковой переменной msg . Следовательно, $ -msg дает длину строки. Мы также можем написать

msg db 'Hello, world!',0xa ;our dear string
len equ 13                 ;length of our dear string

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

Например —

message DB 'I am loving it!', 0

Строковые Инструкции

Каждая строковая инструкция может требовать исходного операнда, целевого операнда или обоих. Для 32-битных сегментов строковые инструкции используют регистры ESI и EDI для указания на операнды источника и назначения соответственно.

Однако для 16-битных сегментов регистры SI и DI используются для указания на источник и пункт назначения соответственно.

Существует пять основных инструкций для обработки строк. Они —

  • MOVS — эта инструкция перемещает 1 байт, слово или двойное слово данных из ячейки памяти в другую.

  • LODS — эта инструкция загружается из памяти. Если операнд имеет один байт, он загружается в регистр AL, если операнд — одно слово, он загружается в регистр AX и двойное слово загружается в регистр EAX.

  • STOS — эта инструкция сохраняет данные из регистра (AL, AX или EAX) в памяти.

  • CMPS — эта инструкция сравнивает два элемента данных в памяти. Данные могут иметь размер в байтах, слово или двойное слово.

  • SCAS — эта инструкция сравнивает содержимое регистра (AL, AX или EAX) с содержимым элемента в памяти.

MOVS — эта инструкция перемещает 1 байт, слово или двойное слово данных из ячейки памяти в другую.

LODS — эта инструкция загружается из памяти. Если операнд имеет один байт, он загружается в регистр AL, если операнд — одно слово, он загружается в регистр AX и двойное слово загружается в регистр EAX.

STOS — эта инструкция сохраняет данные из регистра (AL, AX или EAX) в памяти.

CMPS — эта инструкция сравнивает два элемента данных в памяти. Данные могут иметь размер в байтах, слово или двойное слово.

SCAS — эта инструкция сравнивает содержимое регистра (AL, AX или EAX) с содержимым элемента в памяти.

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

В этих инструкциях используется пара регистров ES: DI и DS: SI, где регистры DI и SI содержат действительные адреса смещения, которые ссылаются на байты, хранящиеся в памяти. SI обычно ассоциируется с DS (сегмент данных), а DI всегда ассоциируется с ES (дополнительный сегмент).

Регистры DS: SI (или ESI) и ES: DI (или EDI) указывают на операнды источника и назначения соответственно. Предполагается, что операндом-источником является DS: SI (или ESI), а операндом-адресатом — ES: DI (или EDI) в памяти.

Для 16-битных адресов используются регистры SI и DI, а для 32-битных адресов используются регистры ESI и EDI.

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

Основная инструкция Операнды в Байт Операция Слово Операция Операция двойного слова
MOVS ES: DI, DS: SI MOVSB MOVSW MOVSD
LODS AX, DS: SI LODSB LODSW LODSD
STOS ES: DI, AX STOSB STOSW STOSD
CMPS DS: SI, ES: DI CMPSB CMPSW CMPSD
SCAS ES: DI, AX SCASB SCASW SCASD

Повторяющиеся префиксы

Префикс REP, если он установлен перед строковой инструкцией, например — REP MOVSB, вызывает повторение инструкции на основе счетчика, размещенного в регистре CX. REP выполняет инструкцию, уменьшает CX на 1 и проверяет, равен ли CX нулю. Он повторяет обработку инструкций, пока CX не станет равным нулю.

Флаг направления (DF) определяет направление операции.

  • Используйте CLD (сбросить флаг направления, DF = 0), чтобы выполнить операцию слева направо.
  • Используйте STD (Установить флаг направления, DF = 1), чтобы выполнить операцию справа налево.

Префикс REP также имеет следующие варианты:

  • РЭП: Это безусловное повторение. Он повторяет операцию, пока CX не станет равным нулю.

  • REPE или REPZ: это условное повторение. Он повторяет операцию, в то время как нулевой флаг указывает на равенство / ноль Он останавливается, когда ZF указывает не равно / ноль или когда CX равен нулю.

  • REPNE или REPNZ: это также условное повторение. Он повторяет операцию, в то время как нулевой флаг указывает не равно / ноль. Он останавливается, когда ZF указывает на равенство / ноль или когда CX уменьшается до нуля.

РЭП: Это безусловное повторение. Он повторяет операцию, пока CX не станет равным нулю.

REPE или REPZ: это условное повторение. Он повторяет операцию, в то время как нулевой флаг указывает на равенство / ноль Он останавливается, когда ZF указывает не равно / ноль или когда CX равен нулю.

REPNE или REPNZ: это также условное повторение. Он повторяет операцию, в то время как нулевой флаг указывает не равно / ноль. Он останавливается, когда ZF указывает на равенство / ноль или когда CX уменьшается до нуля.

Сборка — Массивы

Мы уже обсуждали, что директивы определения данных ассемблеру используются для выделения памяти для переменных. Переменная также может быть инициализирована с определенным значением. Инициализированное значение может быть указано в шестнадцатеричной, десятичной или двоичной форме.

Например, мы можем определить переменную слова ‘months’ любым из следующих способов —

MONTHS	DW	12
MONTHS	DW	0CH
MONTHS	DW	0110B

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

NUMBERS	DW  34,  45,  56,  67,  75, 89

Вышеприведенное определение объявляет массив из шести слов, каждое из которых инициализируется числами 34, 45, 56, 67, 75, 89. Это выделяет 2×6 = 12 байтов последовательного пространства памяти. Символьный адрес первого числа будет НОМЕРА, а второго числа — НОМЕР + 2 и так далее.

Давайте рассмотрим другой пример. Вы можете определить массив с именем инвентарь размера 8 и инициализировать все значения с нуля, как —

INVENTORY   DW  0
            DW  0
            DW  0
            DW  0
            DW  0
            DW  0
            DW  0
            DW  0

Который может быть сокращен как —

INVENTORY   DW  0, 0 , 0 , 0 , 0 , 0 , 0 , 0

Директива TIMES также может использоваться для нескольких инициализаций одного и того же значения. Используя TIMES, массив INVENTORY можно определить как:

INVENTORY TIMES 8 DW 0

пример

В следующем примере демонстрируются вышеуказанные концепции, определяя массив из трех элементов x, в котором хранятся три значения: 2, 3 и 4. Он добавляет значения в массив и отображает сумму 9 —

Live Demo

section	.text
   global _start   ;must be declared for linker (ld)
	
_start:	
 		
   mov  eax,3      ;number bytes to be summed 
   mov  ebx,0      ;EBX will store the sum
   mov  ecx, x     ;ECX will point to the current element to be summed

top:  add  ebx, [ecx]

   add  ecx,1      ;move pointer to next element
   dec  eax        ;decrement counter
   jnz  top        ;if counter not 0, then loop again

done: 

   add   ebx, '0'
   mov  [sum], ebx ;done, store result in "sum"

display:

   mov  edx,1      ;message length
   mov  ecx, sum   ;message to write
   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

section	.data
global x
x:    
   db  2
   db  4
   db  3

sum: 
   db  0

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

9

Сборка — Процедуры

Процедуры или подпрограммы очень важны для ассемблера, так как программы на ассемблере, как правило, имеют большой размер. Процедуры идентифицируются по имени. После этого названия описывается тело процедуры, которая выполняет четко определенную работу. Конец процедуры указывается в декларации возврата.

Синтаксис

Ниже приведен синтаксис для определения процедуры:

proc_name:
   procedure body
   ...
   ret

Процедура вызывается из другой функции с помощью инструкции CALL. Инструкция CALL должна иметь имя вызываемой процедуры в качестве аргумента, как показано ниже —

CALL proc_name

Вызываемая процедура возвращает управление вызывающей процедуре с помощью инструкции RET.

пример

Давайте напишем очень простую процедуру с именем sum, которая добавляет переменные, хранящиеся в регистре ECX и EDX, и возвращает сумму в регистр EAX —

Live Demo

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   mov	ecx,'4'
   sub     ecx, '0'
	
   mov 	edx, '5'
   sub     edx, '0'
	
   call    sum          ;call sum procedure
   mov 	[res], eax
   mov	ecx, msg	
   mov	edx, len
   mov	ebx,1	        ;file descriptor (stdout)
   mov	eax,4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	ecx, res
   mov	edx, 1
   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
sum:
   mov     eax, ecx
   add     eax, edx
   add     eax, '0'
   ret
	
section .data
msg db "The sum is:", 0xA,0xD 
len equ $- msg   

segment .bss
res resb 1

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

The sum is:
9

Структура данных стеков

Стек — это массив данных в виде массива в памяти, в котором данные могут храниться и удаляться из места, называемого «вершиной» стека. Данные, которые должны быть сохранены, «помещаются» в стек, а извлекаемые данные «выталкиваются» из стека. Стек — это структура данных LIFO, т. Е. Данные, сохраненные первыми, извлекаются последними.

Язык ассемблера предоставляет две инструкции для операций со стеком: PUSH и POP. Эти инструкции имеют синтаксис, такой как —

PUSH    operand
POP     address/register

Пространство памяти, зарезервированное в сегменте стека, используется для реализации стека. Регистры SS и ESP (или SP) используются для реализации стека. На вершину стека, которая указывает на последний элемент данных, вставленный в стек, указывает регистр SS: ESP, где регистр SS указывает на начало сегмента стека, а SP (или ESP) дает смещение в сегмент стека.

Реализация стека имеет следующие характеристики —

  • В стек могут быть сохранены только слова или двойные слова , а не байт.

  • Стек растет в обратном направлении, т. Е. К младшему адресу памяти

  • Вершина стека указывает на последний элемент, вставленный в стек; он указывает на младший байт последнего вставленного слова.

В стек могут быть сохранены только слова или двойные слова , а не байт.

Стек растет в обратном направлении, т. Е. К младшему адресу памяти

Вершина стека указывает на последний элемент, вставленный в стек; он указывает на младший байт последнего вставленного слова.

Как мы уже говорили о хранении значений регистров в стеке, прежде чем использовать их для некоторого использования; это можно сделать следующим образом —

; Save the AX and BX registers in the stack
PUSH    AX
PUSH    BX

; Use the registers for other purpose
MOV	AX, VALUE1
MOV 	BX, VALUE2
...
MOV 	VALUE1, AX
MOV	VALUE2, BX

; Restore the original values
POP	AX
POP	BX

пример

Следующая программа отображает весь набор символов ASCII. Основная программа вызывает процедуру с именем display , которая отображает набор символов ASCII.

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   call    display
   mov	eax,1	        ;system call number (sys_exit)
   int	0x80	        ;call kernel
	
display:
   mov    ecx, 256
	
next:
   push    ecx
   mov     eax, 4
   mov     ebx, 1
   mov     ecx, achar
   mov     edx, 1
   int     80h
	
   pop     ecx	
   mov	dx, [achar]
   cmp	byte [achar], 0dh
   inc	byte [achar]
   loop    next
   ret
	
section .data
achar db '0'  

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}
...
...

Сборка — Рекурсия

Рекурсивная процедура — это та, которая вызывает сама себя. Существует два вида рекурсии: прямой и косвенный. При прямой рекурсии процедура вызывает себя, а при косвенной рекурсии первая процедура вызывает вторую процедуру, которая, в свою очередь, вызывает первую процедуру.

Рекурсию можно наблюдать в многочисленных математических алгоритмах. Например, рассмотрим случай вычисления факториала числа. Факториал числа задается уравнением —

Fact (n) = n * fact (n-1) for n > 0

Например: факториал 5 равен 1 x 2 x 3 x 4 x 5 = 5 x факториал 4, и это может быть хорошим примером демонстрации рекурсивной процедуры. Каждый рекурсивный алгоритм должен иметь конечное условие, т. Е. Рекурсивный вызов программы должен быть остановлен при выполнении условия. В случае факторного алгоритма конечное условие достигается, когда n равно 0.

Следующая программа показывает, как факториал n реализован на ассемблере. Для простоты программы мы вычислим факториал 3.

Live Demo

section	.text
   global _start         ;must be declared for using gcc
	
_start:                  ;tell linker entry point

   mov bx, 3             ;for calculating factorial 3
   call  proc_fact
   add   ax, 30h
   mov  [fact], ax
    
   mov	  edx,len        ;message length
   mov	  ecx,msg        ;message to write
   mov	  ebx,1          ;file descriptor (stdout)
   mov	  eax,4          ;system call number (sys_write)
   int	  0x80           ;call kernel

   mov   edx,1            ;message length
   mov	  ecx,fact       ;message to write
   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
	
proc_fact:
   cmp   bl, 1
   jg    do_calculation
   mov   ax, 1
   ret
	
do_calculation:
   dec   bl
   call  proc_fact
   inc   bl
   mul   bl        ;ax = al * bl
   ret

section	.data
msg db 'Factorial 3 is:',0xa	
len equ $ - msg			

section .bss
fact resb 1

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

Factorial 3 is:
6

Сборка — Макросы

Написание макроса — это еще один способ обеспечения модульного программирования на ассемблере.

  • Макрос — это последовательность инструкций, присваиваемая именем и может использоваться в любом месте программы.

  • В NASM макросы определяются с помощью директив % macro и % endmacro .

  • Макрос начинается с директивы% macro и заканчивается директивой% endmacro.

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

В NASM макросы определяются с помощью директив % macro и % endmacro .

Макрос начинается с директивы% macro и заканчивается директивой% endmacro.

Синтаксис для определения макроса —

%macro macro_name  number_of_params
<macro body>
%endmacro

Где number_of_params указывает параметры числа, а macro_name указывает имя макроса.

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

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

mov	edx,len	    ;message length
mov	ecx,msg	    ;message to write
mov	ebx,1       ;file descriptor (stdout)
mov	eax,4       ;system call number (sys_write)
int	0x80        ;call kernel

В приведенном выше примере отображения строки символов регистры EAX, EBX, ECX и EDX были использованы вызовом функции INT 80H. Таким образом, каждый раз, когда вам нужно отобразить на экране, вам нужно сохранить эти регистры в стеке, вызвать INT 80H и затем восстановить исходное значение регистров из стека. Поэтому было бы полезно написать два макроса для сохранения и восстановления данных.

Мы заметили, что некоторые инструкции, такие как IMUL, IDIV, INT и т. Д., Нуждаются в том, чтобы некоторая информация была сохранена в некоторых конкретных регистрах и даже возвращала значения в некоторых конкретных регистрах. Если программа уже использовала эти регистры для хранения важных данных, то существующие данные из этих регистров должны быть сохранены в стеке и восстановлены после выполнения инструкции.

пример

В следующем примере показано определение и использование макросов —

Live Demo

; A macro with two parameters
; Implements the write system call
   %macro write_string 2 
      mov   eax, 4
      mov   ebx, 1
      mov   ecx, %1
      mov   edx, %2
      int   80h
   %endmacro
 
section	.text
   global _start            ;must be declared for using gcc
	
_start:                     ;tell linker entry point
   write_string msg1, len1               
   write_string msg2, len2    
   write_string msg3, len3  
	
   mov eax,1                ;system call number (sys_exit)
   int 0x80                 ;call kernel

section	.data
msg1 db	'Hello, programmers!',0xA,0xD 	
len1 equ $ - msg1			

msg2 db 'Welcome to the world of,', 0xA,0xD 
len2 equ $- msg2 

msg3 db 'Linux assembly programming! '
len3 equ $- msg3

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

Hello, programmers!
Welcome to the world of,
Linux assembly programming!

Сборка — Управление файлами

Система рассматривает любые входные или выходные данные как поток байтов. Есть три стандартных файловых потока —

  • Стандартный ввод (стандартный),
  • Стандартный вывод (стандартный вывод) и
  • Стандартная ошибка (stderr).

Дескриптор файла

Дескриптор файла — это 16-разрядное целое число, назначаемое файлу в качестве идентификатора файла. Когда создается новый файл или открывается существующий файл, дескриптор файла используется для доступа к файлу.

Файловый дескриптор стандартных файловых потоков — stdin, stdout и stderr — 0, 1 и 2 соответственно.

Файловый указатель

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

Системные вызовы для работы с файлами

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

% е название % EBX % ECX % EDX
2 sys_fork struct pt_regs
3 sys_read без знака int символ * size_t
4 sys_write без знака int const char * size_t
5 sys_open const char * ИНТ ИНТ
6 sys_close без знака int
8 sys_creat const char * ИНТ
19 sys_lseek без знака int off_t без знака int

Шаги, необходимые для использования системных вызовов, такие же, как мы обсуждали ранее —

  • Поместите номер системного вызова в регистр EAX.
  • Сохраните аргументы системного вызова в регистрах EBX, ECX и т. Д.
  • Вызовите соответствующее прерывание (80ч).
  • Результат обычно возвращается в регистр EAX.

Создание и открытие файла

Для создания и открытия файла выполните следующие задачи:

  • Поместите системный вызов sys_creat () номер 8 в регистр EAX.
  • Поместите имя файла в регистр EBX.
  • Поместите права доступа к файлу в регистр ECX.

Системный вызов возвращает файловый дескриптор созданного файла в регистре EAX, в случае ошибки код ошибки находится в регистре EAX.

Открытие существующего файла

Чтобы открыть существующий файл, выполните следующие задачи:

  • Поместите системный вызов sys_open () номер 5 в регистр EAX.
  • Поместите имя файла в регистр EBX.
  • Поместите режим доступа к файлу в регистр ECX.
  • Поместите права доступа к файлу в регистр EDX.

Системный вызов возвращает файловый дескриптор созданного файла в регистре EAX, в случае ошибки код ошибки находится в регистре EAX.

Среди режимов доступа к файлам чаще всего используются: только чтение (0), только запись (1) и чтение-запись (2).

Чтение из файла

Для чтения из файла выполните следующие задачи:

  • Поместите системный вызов sys_read () номер 3 в регистр EAX.

  • Поместите дескриптор файла в регистр EBX.

  • Поместите указатель на входной буфер в регистр ECX.

  • Поместите размер буфера, т. Е. Количество байтов для чтения, в регистр EDX.

Поместите системный вызов sys_read () номер 3 в регистр EAX.

Поместите дескриптор файла в регистр EBX.

Поместите указатель на входной буфер в регистр ECX.

Поместите размер буфера, т. Е. Количество байтов для чтения, в регистр EDX.

Системный вызов возвращает количество байтов, прочитанных в регистре EAX, в случае ошибки код ошибки находится в регистре EAX.

Запись в файл

Для записи в файл выполните следующие задачи:

  • Поместите системный вызов sys_write () номер 4 в регистр EAX.

  • Поместите дескриптор файла в регистр EBX.

  • Поместите указатель на выходной буфер в регистр ECX.

  • Поместите размер буфера, т. Е. Количество байтов для записи, в регистр EDX.

Поместите системный вызов sys_write () номер 4 в регистр EAX.

Поместите дескриптор файла в регистр EBX.

Поместите указатель на выходной буфер в регистр ECX.

Поместите размер буфера, т. Е. Количество байтов для записи, в регистр EDX.

Системный вызов возвращает фактическое количество байтов, записанных в регистр EAX, в случае ошибки код ошибки находится в регистре EAX.

Закрытие файла

Для закрытия файла выполните следующие задачи:

  • Поместите системный вызов sys_close () номер 6 в регистр EAX.
  • Поместите дескриптор файла в регистр EBX.

Системный вызов возвращает, в случае ошибки, код ошибки в регистре EAX.

Обновление файла

Для обновления файла выполните следующие задачи:

  • Поместите системный вызов sys_lseek () номер 19 в регистр EAX.
  • Поместите дескриптор файла в регистр EBX.
  • Поместите значение смещения в регистр ECX.
  • Поместите референтную позицию для смещения в регистр EDX.

Исходная позиция может быть:

  • Начало файла — значение 0
  • Текущая позиция — значение 1
  • Конец файла — значение 2

Системный вызов возвращает, в случае ошибки, код ошибки в регистре EAX.

пример

Следующая программа создает и открывает файл с именем myfile.txt и записывает текст «Welcome to Tutorials Point» в этом файле. Затем программа читает файл и сохраняет данные в буфере с именем info . Наконец, он отображает текст как сохраненный в информации .

section	.text
   global _start         ;must be declared for using gcc
	
_start:                  ;tell linker entry point
   ;create the file
   mov  eax, 8
   mov  ebx, file_name
   mov  ecx, 0777        ;read, write and execute by all
   int  0x80             ;call kernel
	
   mov [fd_out], eax
    
   ; write into the file
   mov	edx,len          ;number of bytes
   mov	ecx, msg         ;message to write
   mov	ebx, [fd_out]    ;file descriptor 
   mov	eax,4            ;system call number (sys_write)
   int	0x80             ;call kernel
	
   ; close the file
   mov eax, 6
   mov ebx, [fd_out]
    
   ; write the message indicating end of file write
   mov eax, 4
   mov ebx, 1
   mov ecx, msg_done
   mov edx, len_done
   int  0x80
    
   ;open the file for reading
   mov eax, 5
   mov ebx, file_name
   mov ecx, 0             ;for read only access
   mov edx, 0777          ;read, write and execute by all
   int  0x80
	
   mov  [fd_in], eax
    
   ;read from file
   mov eax, 3
   mov ebx, [fd_in]
   mov ecx, info
   mov edx, 26
   int 0x80
    
   ; close the file
   mov eax, 6
   mov ebx, [fd_in]
   int  0x80 
	
   ; print the info 
   mov eax, 4
   mov ebx, 1
   mov ecx, info
   mov edx, 26
   int 0x80
       
   mov	eax,1             ;system call number (sys_exit)
   int	0x80              ;call kernel

section	.data
file_name db 'myfile.txt'
msg db 'Welcome to Tutorials Point'
len equ  $-msg

msg_done db 'Written to file', 0xa
len_done equ $-msg_done

section .bss
fd_out resb 1
fd_in  resb 1
info resb  26

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —

Written to file
Welcome to Tutorials Point

Сборка — Управление памятью

Системный вызов sys_brk () предоставляется ядром для выделения памяти без необходимости ее перемещения позже. Этот вызов выделяет память прямо за изображением приложения в памяти. Эта системная функция позволяет вам установить максимальный доступный адрес в разделе данных.

Этот системный вызов принимает один параметр, который является наибольшим адресом памяти, который необходимо установить. Это значение сохраняется в регистре EBX.

В случае любой ошибки sys_brk () возвращает -1 или возвращает сам отрицательный код ошибки. В следующем примере демонстрируется динамическое распределение памяти.

пример

Следующая программа выделяет 16 КБ памяти с помощью системного вызова sys_brk () —

Live Demo

section	.text
   global _start         ;must be declared for using gcc
	
_start:	                 ;tell linker entry point

   mov	eax, 45		 ;sys_brk
   xor	ebx, ebx
   int	80h

   add	eax, 16384	 ;number of bytes to be reserved
   mov	ebx, eax
   mov	eax, 45		 ;sys_brk
   int	80h
	
   cmp	eax, 0
   jl	exit	;exit, if error 
   mov	edi, eax	 ;EDI = highest available address
   sub	edi, 4		 ;pointing to the last DWORD  
   mov	ecx, 4096	 ;number of DWORDs allocated
   xor	eax, eax	 ;clear eax
   std			 ;backward
   rep	stosd            ;repete for entire allocated area
   cld			 ;put DF flag to normal state
	
   mov	eax, 4
   mov	ebx, 1
   mov	ecx, msg
   mov	edx, len
   int	80h		 ;print a message

exit:
   mov	eax, 1
   xor	ebx, ebx
   int	80h
	
section	.data
msg    	db	"Allocated 16 kb of memory!", 10
len     equ	$ - msg

Когда приведенный выше код компилируется и выполняется, он дает следующий результат —