Linguagem de Montagem

Propaganda
Linguagem de Montagem
Procedimentos e a Pilha
Slides baseados em material associado ao livro Introduction to Assembly Language
Programming, Sivarama Dandamudi
1
O que é a pilha?
n 
n 
n 
n 
A pilha é uma estrutura de dados do tipo LIFO
(last-in-first-out)
Se vista como um array de elementos, as
operações de inserção e remoção ocorrem
somente de um lado do array
Apenas o elemento no topo da pilha (TOS) está
diretamente acessível
Existem duas operações básicas:
n 
n 
push (inserção)
pop (remoção)
2
Exemplo de pilha
inserção remoção 3
Implementação da pilha no Pentium
n 
O segmento de pilha é usado para
implementar a pilha
n 
n 
n 
Os registradores SS e SP são usados
SS:SP endereça o topo da pilha
Características da pilha no Pentium
n 
n 
n 
Apenas palavras de 16 bits ou 32 bits podem ser
salvas na pilha, nunca um byte simples
A pilha cresce em direção ao menor endereço (ou
seja, de “cabeça para baixo”)
O topo da pilha (TOS) sempre aponta para o último
elemento inserido
4
Instruções de pilha
n 
O Pentium provê duas instruções básicas:
push
pop
n 
fonte e destino podem ser:
n 
n 
n 
n 
fonte
destino
Registradores de uso geral de 16 ou 32 bits
Um registrador de segmento
Um dado de memória de 16 ou 32 bits
O fonte de push pode também ser um
operando imediato de tamanho 16 ou 32 bits
Uma pilha vazia, de 256 bytes, é criada com:
.STACK 100H
5
Pilha no Pentium – Exemplo 1
push
21ABH
push 7FBD329AH
Inserção na pilha cheia resultará overflow 6
Pilha no Pentium – Exemplo 2
pop EBX
Remoção em pilha vazia resulta em underflow resíduo 7
Tabela de operações da pilha
8
Instruções de pilha adicionais
n 
Operações de pilha sobre os FLAGS
n 
n 
n 
n 
As instruções push e pop não podem ser usadas com
o registrador de flags
Existem duas instruções especiais para este fim:
pushf (empilha os flags de 16 bits)
popf
(desempilha os flags de 16 bits)
Nenhum operando é requerido
pushfd e popfd devem ser usadas para os flags de
32 bits (EFLAGS)
9
Instruções de pilha adicionais (cont.)
n 
Operações de pilha sobre os 8 registradores de
uso geral
n 
n 
n 
n 
As instruções pusha e popa podem ser usadas
para salvar os 8 registradores de uso geral
AX, BX, CX, DX, SP, BP, SI e DI
pusha empilha os oito registradores na ordem
acima (AX primeiro e DI por último)
popa desempilha os registradores, exceto que o
valor de SP não é carregado no registrador SP
Estas instruções são úteis na entrada de
procedimentos para liberar os registradores de uso
geral e depois recuperar seus valores
10
Usos da pilha
n 
Três usos principais
n 
Armazenamento temporário de dados
n 
Transferência de controle
n 
Passagem de parâmetros (chamada de procedimento)
11
Usos da pilha – Armazenamento temporário
n 
Trocar o valor entre duas posições de
memória pode ser feito usando a pilha
como área temporária
Dois regist=adores uso geral Quat=o acessos na memória push
push
pop
pop
valor1
valor2
valor1
valor2
Somente Quat=o acessos na memória 12
Usos da pilha – Armazenamento temporário (cont.)
n 
Frequentemente usada para salvar o valor dos
registradores
; salva BX e CX na pilha
push
BX
push
CX
....
<< BX e CX podem ser usados agora>>
....
; recupra o valor de BX e CX
pop
CX
pop
BX
13
Usos da pilha (cont.)
Transferência de controle
n 
n 
Em chamadas de procedimentos e interrupções, o
endereço de retorno é armazenado na pilha
A apresentação de procedimentos mais adiante irá
esclarecer esta possibilidade
Passagem de Parâmetros
n 
n 
A pilha é extensivamente usada para a passagem de
parâmetros em chamadas de procedimentos
Mais adiante veremos como isso é possível
14
Diretivas para Procedimentos
n 
n 
A LA provê duas diretivas para chamadas de
procedimentos: PROC (inicio) e ENDP (fim)
Opcionalmente, para definir um procedimento NEAR
(“próximo”), usamos:
nome_proc
n 
n 
PROC
[NEAR]
Em procedimentos do tipo NEAR, ambos, código que chama e
procedimento chamado devem ocupar o mesmo segmento de
código
Opcionalmente, um procedimento FAR (“distante”) pode
ser definido como:
nome_proc
n 
PROC
[FAR]
Em procedimentos do tipo FAR, o procedimento chamado e o
procedimento que chama, ocupam dois segmentos distintos de
código
15
Diretivas para Procedimentos (cont.)
n 
n 
n 
Se FAR ou NEAR não for especificado, é assumido
NEAR (ou seja, NEAR é o default)
Trabalharemos apenas com procedimentos do tipo
NEAR
Uma definição típica de procedimento NEAR:
nome_proc
. . .
<< corpo do
. . .
nome_proc
n 
PROC
.
procedimento >>
.
ENDP
nome_proc deve ser igual nas diretivas PROC e ENDP
16
Instruções para Procedimentos
n 
n 
n 
O Pentium provê duas instruções para a chamada e
retorno dos procedimentos: call e ret
A instrução call é usada para invocar um
procedimento
Formato:
call nome_proc
n 
n 
nome_proc é o nome do procedimento
Ações executadas durante uma chamada a
procedimentos do tipo NEAR
SP := SP - 2
; reserva espaço na pilha da memória
(SS:SP) := IP
; empilha o endereço atual de IP
IP := IP + deslocamento_relativo_do_procedimento
17
Instruções para Procedimentos (cont.)
n 
n 
A instrução ret deve ser usada para devolver o
controle da execução de volta para o procedimento
que chamou
Como o processador sabe o endereço de retorno?
n 
n 
n 
Ele usa o endereço de retorno colocado na pilha como
parte da execução da instrução call de chamada de
procedimentos
É fundamental que o topo da pilha (TOS) esteja apontando
para o endereço de retorno na execução da instrução ret
Ações tomadas na execução da instrução ret:
IP := (SS:SP)
; desempilha o endereço de retorno
SP := SP + 2
; atualiza o topo da pilha
18
Instruções para Procedimentos (cont.)
n 
É possível especificar um parâmetro opcional inteiro
para a instrução ret
n 
n 
n 
O formato é:
ret
parâmetro_opcional
Exemplo:
ret 6
As ações tomadas com a execução de ret com
parâmetro opcional são:
IP := (SS:SP)
; desempilha o endereço de retorno
SP := SP + 2 + parametro_opcional ; ex. 6
19
Como é feita a transferência do controle?
Offset(hex) machine code(hex)
cs:000A
cs:000D
opcode cs:0019
cs:0028
cs:002B
ForFard E8C000
8BD8
55
E8EEFF
8BD0
main
PROC
. . . . . .
call
sum
mov
BX,AX
. . . . . .
main
ENDP
“ret” sum
PROC
push
BP
. . . . . .
sum
ENDP
“ret” avg
PROC
. . . . . .
call
sum
mov
DX,AX
. . . . . .
avg
ENDP
“ret” Backward 0019 - 000D = 000C
Push BP offset relativo (liCle endian) IP offset relativo 0019 - 002B = FFEE
Push BP IP offset relativo (comp de 2) 20
Passagem de parâmetros
n 
n 
A passagem de parâmetros em assembly é uma
tarefa diferente e um pouco mais complicada que em
uma linguagem de alto nível
Em assembly:
n 
n 
n 
Tipos de áreas de trabalho usadas
n 
n 
n 
Deve-se primeiro colocar todos os parâmetros em uma área
de memória mutuamente acessível
Então chamar o procedimento
Registradores (são usados registradores de uso geral)
Memória (é usada a pilha)
Métodos comuns de passagem de parâmetros:
n 
n 
Método Registrador
Método Pilha
21
Passagem de parâmetros (cont.)
n 
Método Registrador
n 
n 
O procedimento que chama coloca os parâmetros nos
registradores de uso geral antes de invocar o
procedimento chamado através do comando call
Exemplos
n 
n 
PROGRAMA PROCEX1.ASM
n  Passagem por valor usando registradores
n  Uma rotina simples de soma
PROGRAMA PROCEX2.ASM
n  Passagem por referência usando registradores
n  Uma rotina de cálculo de tamanho de strings
22
Passagem de parâmetros (cont.)
n 
Prós e contras do método registrador
n 
Vantagens
n 
n 
n 
Mais conveniente e fácil
Mais rápido
Desvantagens
n 
n 
Poucos parâmetros podem ser passados
n  Pequeno conjunto de registradores disponível
Frequentemente os registradores de uso geral não estão
livres
n  Usar a pilha para salvar o conteúdo dos registradores
impacta a segunda vantagem
23
Passagem de parâmetros (cont.)
n 
Método Pilha
n 
n 
Todos os parâmetros são empilhados antes de
chamar o procedimento
Exemplo:
push number1
push number2
call soma
24
Acesso aos parâmetros na pilha
n 
Como ter acesso aos parâmetros na pilha?
n 
Podemos usar:
add
mov
SP, 2
BX, [SP]
; arLazenar retorMo (IP) Consome regist=ador de Propósito geral Problema: sujeito a erros
n 
n 
É necessário lembrar de atualizar SP para apontar para o
valor de retorno do procedimento antes do seu término
Existe uma alternativa melhor?
n 
Usar o registrador BP para acessar parâmetros na pilha
25
Acesso aos parâmetros na pilha usando BP reg.
n 
Método preferido de acesso aos parâmetros
n 
Para acessar o número 2 do exemplo anterior:
mov
mov
BP, SP
BX, [BP+2]
Problema: o conteúdo de BP foi perdido!
Necessário preservar o conteúdo de BP
n  Usado nas operações em vários procedimentos (atenção:
valor do deslocamento dos parâmetros é alterado)
push BP
mov
BP, SP
n 
Os valores dos parâmet=os, endereços de retorMo e o BP anterior arLazenados na pilha, são chamados de stack Rame 26
Limpando os parâmetros da pilha
Stack state after
push BP
Stack state after
pop BP
Stack state after
executing ret
27
Limpando os parâmetros da pilha (cont.)
n 
Dois maneiras de limpar os parâmetros
indesejados na pilha
n 
n 
Usar inteiro opcional na instrução ret
ret 4 ; usar no exemplo anterior
Adicionar uma constante para SP na chamada do
procedimento (C usa este método)
Em C:
push
push
call
add
sum(number2,number2);
number1
number2
sum
SP, 4
28
Quem deve limpar os parâmetros indesejados
n 
Na chamada do procedimento
n 
n 
n 
n 
Atualizar SP a cada procedimento chamado
Não é realmente necessário se os procedimentos
utilizarem um número fixo de parâmetros
C utiliza este método pois permite número variável de
parâmetros
No procedimento chamado
n 
n 
O código se torna modular (compensação dos
parâmetros é feito em um só lugar)
Não pode ser usado com um número variável de
parâmetros
29
Preservar o estado do procedimento de chamada
n 
Necessidade de preservar o estado para cada
chamada de procedimento
n 
n 
A pilha é utilizada para este propósito
Que registradores devem ser salvos?
n 
Salvar os que são usados pela chamada de
procedimento mas eles são modificados pelo
procedimento chamado
n 
n 
Pode causar problemas
Salvar todos os registradores (método força bruta)
n 
n 
Feito usando pusha
Incrementa sobrecarga
n  pusha leva 5 clocks para salvar todos os 8 registradores
30
Estado da pilha após usar pusha
O offeset para number1 e number2 foi incrementado 31
Onde deve ser preservado o estado do registrador
n 
Na chamada do procedimento
n 
n 
n 
n 
Precisa conhecer os registradores usados pelo
procedimento chamado
Necessário incluir instruções para salvar e restaurar
os registradores para cada procedimento chamado
Problemas de manutenção no programa
No procedimento chamado
n 
n 
Método preferido pois código se torna modular
(preservação do estado é feito apenas uma vez e em
um lugar)
Evita problemas de manutenção no programa
32
Instruções para Stack Frame
n 
Instrução ENTER
n 
Facilita a alocação de stack frame
enter bytes, level
n Bytes = espaço de armazenamento local
n Level = nível de aninhamento (nós usamos 0)
n 
Exemplo
enter XX, 0
n Equivalente a
push BP
mov BP, SP
sub SP, XX
33
Instruções para Stack Frame (cont.)
n 
Instrução LEAVE
n 
Libera stack frame
leave
Não tem operandos
n 
Equivalente a
mov SP, BP
pop BP
n 
34
Um modelo de procedimentos típico
proc-name
PROC
proc-name
enter
XX,0
. . . . . .
<procedure body>
. . . . . .
leave
ret
YY
ENDP
35
Passagem de parâmetros na pilha: exemplos
n 
PROCEX3.ASM
n 
n 
n 
PROCSWAP.ASM
n 
n 
n 
Chamada por valor usando método pilha
Um simples procedimento de soma
Chamada por referência usando método pilha
Os dois primeiros caracteres da string são trocados
BBLSORT.ASM
n 
n 
Implementa o algoritmo “bubble sort”
Usa pusha e popa para salvar e restaurar
registradores
36
Download