Montagem - Procedimentos
Os procedimentos ou sub-rotinas são muito importantes na linguagem assembly, pois os programas em linguagem assembly tendem a ser grandes. Os procedimentos são identificados por um nome. Seguindo este nome, é descrito o corpo do procedimento que realiza um trabalho bem definido. O fim do procedimento é indicado por uma instrução de retorno.
Sintaxe
A seguir está a sintaxe para definir um procedimento -
proc_name:
procedure body
...
ret
O procedimento é chamado de outra função usando a instrução CALL. A instrução CALL deve ter o nome do procedimento chamado como um argumento conforme mostrado abaixo -
CALL proc_name
O procedimento chamado retorna o controle ao procedimento de chamada usando a instrução RET.
Exemplo
Vamos escrever um procedimento muito simples denominado soma que adiciona as variáveis armazenadas no registro ECX e EDX e retorna a soma no 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
Quando o código acima é compilado e executado, ele produz o seguinte resultado -
The sum is:
9
Estrutura de dados de pilhas
Uma pilha é uma estrutura de dados semelhante a um array na memória, na qual os dados podem ser armazenados e removidos de um local chamado 'topo' da pilha. Os dados que precisam ser armazenados são 'colocados' na pilha e os dados a serem recuperados são 'retirados' da pilha. Stack é uma estrutura de dados LIFO, ou seja, os dados armazenados primeiro são recuperados por último.
A linguagem assembly fornece duas instruções para operações de pilha: PUSH e POP. Essas instruções têm sintaxes como -
PUSH operand
POP address/register
O espaço de memória reservado no segmento da pilha é usado para implementar a pilha. Os registros SS e ESP (ou SP) são usados para implementar a pilha. O topo da pilha, que aponta para o último item de dados inserido na pilha, é apontado pelo registro SS: ESP, onde o registro SS aponta para o início do segmento da pilha e o SP (ou ESP) fornece o deslocamento para o segmento da pilha.
A implementação da pilha tem as seguintes características -
Somente words ou doublewords pode ser salvo na pilha, não em um byte.
A pilha cresce na direção inversa, ou seja, em direção ao endereço de memória inferior
O topo da pilha aponta para o último item inserido na pilha; ele aponta para o byte inferior da última palavra inserida.
Conforme discutimos sobre como armazenar os valores dos registradores na pilha antes de usá-los para algum uso; isso pode ser feito da seguinte maneira -
; 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
Exemplo
O programa a seguir exibe todo o conjunto de caracteres ASCII. O programa principal chama um procedimento denominado display , que exibe o conjunto 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'
Quando o código acima é compilado e executado, ele produz o seguinte resultado -
0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}
...
...