Учебники

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

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

Синтаксис

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

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'  

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