Сборка - Процедуры
Процедуры или подпрограммы очень важны для языка ассемблера, поскольку программы на языке ассемблера имеют тенденцию быть большими по размеру. Процедуры идентифицируются по имени. После этого имени описывается тело процедуры, которая выполняет четко определенную работу. Конец процедуры обозначается оператором возврата.
Синтаксис
Ниже приводится синтаксис для определения процедуры -
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) дает смещение в сегмент стека.
Реализация стека имеет следующие характеристики -
Только words или doublewords можно сохранить в стек, а не байт.
Стек растет в обратном направлении, т. Е. В сторону младшего адреса памяти.
Верх стека указывает на последний элемент, вставленный в стек; он указывает на младший байт последнего вставленного слова.
Как мы уже говорили о хранении значений регистров в стеке перед их использованием для некоторых целей; это можно сделать следующим образом -
; 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 BX
POP AX
пример
Следующая программа отображает весь набор символов 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{|}
...
...