Процедуры или подпрограммы очень важны для ассемблера, так как программы на ассемблере, как правило, имеют большой размер. Процедуры идентифицируются по имени. После этого названия описывается тело процедуры, которая выполняет четко определенную работу. Конец процедуры указывается в декларации возврата.
Синтаксис
Ниже приведен синтаксис для определения процедуры:
proc_name: procedure body ... ret
Процедура вызывается из другой функции с помощью инструкции CALL. Инструкция CALL должна иметь имя вызываемой процедуры в качестве аргумента, как показано ниже —
CALL proc_name
Вызываемая процедура возвращает управление вызывающей процедуре с помощью инструкции RET.
пример
Давайте напишем очень простую процедуру с именем sum, которая добавляет переменные, хранящиеся в регистре ECX и EDX, и возвращает сумму в регистр EAX —
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'
Когда приведенный выше код компилируется и выполняется, он дает следующий результат —