Montaje - Procedimientos

Los procedimientos o subrutinas son muy importantes en lenguaje ensamblador, ya que los programas en lenguaje ensamblador tienden a ser de gran tamaño. Los procedimientos se identifican con un nombre. A continuación de este nombre, se describe el cuerpo del procedimiento que realiza un trabajo bien definido. El final del procedimiento se indica mediante una declaración de devolución.

Sintaxis

A continuación se muestra la sintaxis para definir un procedimiento:

proc_name:
   procedure body
   ...
   ret

El procedimiento se llama desde otra función mediante la instrucción CALL. La instrucción CALL debe tener el nombre del procedimiento llamado como argumento, como se muestra a continuación:

CALL proc_name

El procedimiento llamado devuelve el control al procedimiento de llamada utilizando la instrucción RET.

Ejemplo

Escribamos un procedimiento muy simple llamado suma que suma las variables almacenadas en el registro ECX y EDX y devuelve la suma en el registro 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

Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:

The sum is:
9

Estructura de datos de pilas

Una pila es una estructura de datos similar a una matriz en la memoria en la que los datos se pueden almacenar y eliminar de una ubicación llamada 'parte superior' de la pila. Los datos que deben almacenarse se 'empujan' a la pila y los datos que se van a recuperar se 'extraen' de la pila. Stack es una estructura de datos LIFO, es decir, los datos almacenados primero se recuperan en último lugar.

El lenguaje ensamblador proporciona dos instrucciones para las operaciones de pila: PUSH y POP. Estas instrucciones tienen sintaxis como:

PUSH    operand
POP     address/register

El espacio de memoria reservado en el segmento de la pila se utiliza para implementar la pila. Los registros SS y ESP (o SP) se utilizan para implementar la pila. La parte superior de la pila, que apunta al último elemento de datos insertado en la pila, es apuntada por el registro SS: ESP, donde el registro SS apunta al comienzo del segmento de la pila y el SP (o ESP) da el desplazamiento en el segmento de pila.

La implementación de la pila tiene las siguientes características:

  • Solamente words o doublewords podría guardarse en la pila, no en un byte.

  • La pila crece en la dirección inversa, es decir, hacia la dirección de memoria inferior

  • La parte superior de la pila apunta al último elemento insertado en la pila; apunta al byte inferior de la última palabra insertada.

Como comentamos sobre almacenar los valores de los registros en la pila antes de usarlos para algún uso; se puede hacer de la siguiente manera:

; 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

Ejemplo

El siguiente programa muestra todo el juego de caracteres ASCII. El programa principal llama a un procedimiento llamado pantalla , que muestra el juego de caracteres 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'

Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:

0123456789:;<=>[email protected][\]^_`abcdefghijklmnopqrstuvwxyz{|}
...
...