Post on 11-Feb-2019
Organização Básica de computadores e linguagem de
montagem
2o Semestre de 2011
Prof. Edson Borin
Procedimentos e Funções
Procedimentos
Capítulo 4
Procedimentos e funções
Neste capítulo vamos estudar as facilidades que os processadores oferecem para aimplementação do conceito de procedimentos e funções por linguagens de programa-ção. Considere o trecho de programa abaixo que inclui um procedimento denominadotroca.
1 void troca(int *a, int *b)2 {3 int tmp;45 tmp = *a;6 *a = *b;7 *b = tmp;8 }9
10 int main(void)11 {12 int x,y,z;1314 ...15 troca(&x,&y);16 z=x+1;17 troca(&z,&x);18 x=y-1;19 ...20 }
A tabela abaixo indica a sequência de comandos executados durante as duasinvocações do procedimento troca mostradas no trecho de programa:
9
Uma solução (Antiga)
10 CAPÍTULO 4. PROCEDIMENTOS E FUNÇÕES
Linhas Comentário
15 primeira invocação de troca5, 6, 7 execução dos comandos do procedimento
16 retorna do procedimento e executa este comando
17 segunda invocação de troca5, 6, 7 execução dos comandos do procedimento
18 retorna do procedimento e executa este comando
Ou seja, como esperado, a cada invocação do procedimento o fluxo de execução
desvia para o início do procedimento, e ao final da execução do procedimento deve
ser feito um desvio para o comando seguinte à chamada de procedimento: após a
execução do comando na linha 7 deve ser executado o comando na linha 16 na pri-
meira invocação; na segunda invocação, após a execução do comando na linha 7 deve
ser executado o comando na linha 18. E aí aparece a dificuldade de se implementar
procedimentos com as instruções de desvio que vimos até agora: a cada chamada
de procedimento, o desvio ao término do procedimento deve ser feito para endereços
diferentes.
Uma primeira abordagem para a implementação de procedimentos é a seguinte.
A chamada de procedimento é implementada por uma instrução especial que arma-
zena o valor de ip corrente na primeira palavra do procedimento (note que neste
momento ip já foi incrementado e contém o valor do endereço da instrução seguinte
à chamada de procedimento, ou seja, ip contém o endereço de retorno). Mais especi-
ficamente, suponha que exista uma instrução especial JSR (desvia para subrotina –
em inglês, jump to subroutine), que tem um operando, o endereço do procedimento,
definida como:
JSR
Desvia para subrotina
Sintaxe Operação Flags Codificação
jsr expr32mem[imd32]← ip + 8
ip← imd32 + 4–
31 0
xx31 0
imd32
Ao ser executada, essa instrução armazena o valor do endereço de retorno na
primeira palavra do procedimento (assumindo o valor de ip no início da execução da
instrução, o endereço de retorno é textttip+8). O montador deve deixar essa primeira
palavra livre na montagem). Ao final da execução do procedimento, para retornar da
chamada, deve-se desviar para o endereço armazenado na primeira palavra.
Como exemplo, considere o trecho abaixo em linguagem de montagem:
Uma solução (Antiga)
E antes de retornar
11
1 ...2 jsr proc_P ; uma chamada ao procedimento de nome proc_P3 ret_aqui: ; este é o endereço de retorno desta4 ...56 ; esta é a declaração do procedimento7 proc_P:8 ds 4 ; a primeira palavra do procedimento é reservada9 set r0,1 ; a primeira instrução está no endereço
10 ... ; resto do procedimento
Nesse caso, a invocação do procedimento na linha x armazenaria no endereço
proc_P o valor do endereço ret_aqui.
Com esse esquema, quando uma chamada de procedimento é executada, o ende-
reço de retorno para aquela chamada fica armazenado em uma posição de memória
conhecida. O retorno do procedimento pode ser executado pela seguinte sequência
de instruções:
1 ; ...2 ld r1,proc_P ; carrega endereço de retorno previamente3 jmp (r1) ; e salta para esse endereço4 ; ...
Esta solução foi adotada em alguns processadores antigos, como o IBM-1130 da
década de 60. Ela no entanto tem várias limitações. Por exemplo, ela não funciona
se o código do programa está sendo executado a partir de uma memória de leitura
apenas, como ROMs ou EPROMs, muito comuns hoje em qualquer equipamento.
Um outro problema, bem mais grave, é que esta solução não permite recursão: uma
nova chamada ao procedimento de dentro do próprio procedimento faria com que o
endereço de retorno da primeira chamada fosse destruído, já que o endereço dessa
segunda chamada seria armazenado no mesmo lugar que o da primeira chamada.
Na época, uma das linguagens mais utilizadas era FORTRAN, que não permitia
recursão, e essa limitação não era tão incômoda.
Obviamente essa primeira abordagem não é interessante hoje em dia, pois re-
cursão é uma funcionalidade indispensável em linguagens de programação. Como
permitir recursão? Sabemos que recursão envolve o uso de pilhas a solução por-
tanto é o uso de uma pilha pelo processador.
4.0.1 Instruções que manipulam pilhas
Uma pilha pode ser implementada no Faíska com um registrador de propósito geral,
como r0 . Inicialmente, ele deve ser inicializado para apontar para uma região de
memória disponível. Um dado é empilhado na pilha apontada por r0 decrementando-
se r0 de 4 e escrevendo-se o dado na nova posição apontada por r0 (neste esquema,
11
1 ...2 jsr proc_P ; uma chamada ao procedimento de nome proc_P3 ret_aqui: ; este é o endereço de retorno desta4 ...56 ; esta é a declaração do procedimento7 proc_P:8 ds 4 ; a primeira palavra do procedimento é reservada9 set r0,1 ; a primeira instrução está no endereço
10 ... ; resto do procedimento
Nesse caso, a invocação do procedimento na linha x armazenaria no endereço
proc_P o valor do endereço ret_aqui.
Com esse esquema, quando uma chamada de procedimento é executada, o ende-
reço de retorno para aquela chamada fica armazenado em uma posição de memória
conhecida. O retorno do procedimento pode ser executado pela seguinte sequência
de instruções:
1 ; ...2 ld r1,proc_P ; carrega endereço de retorno previamente3 jmp (r1) ; e salta para esse endereço4 ; ...
Esta solução foi adotada em alguns processadores antigos, como o IBM-1130 da
década de 60. Ela no entanto tem várias limitações. Por exemplo, ela não funciona
se o código do programa está sendo executado a partir de uma memória de leitura
apenas, como ROMs ou EPROMs, muito comuns hoje em qualquer equipamento.
Um outro problema, bem mais grave, é que esta solução não permite recursão: uma
nova chamada ao procedimento de dentro do próprio procedimento faria com que o
endereço de retorno da primeira chamada fosse destruído, já que o endereço dessa
segunda chamada seria armazenado no mesmo lugar que o da primeira chamada.
Na época, uma das linguagens mais utilizadas era FORTRAN, que não permitia
recursão, e essa limitação não era tão incômoda.
Obviamente essa primeira abordagem não é interessante hoje em dia, pois re-
cursão é uma funcionalidade indispensável em linguagens de programação. Como
permitir recursão? Sabemos que recursão envolve o uso de pilhas a solução por-
tanto é o uso de uma pilha pelo processador.
4.0.1 Instruções que manipulam pilhas
Uma pilha pode ser implementada no Faíska com um registrador de propósito geral,
como r0 . Inicialmente, ele deve ser inicializado para apontar para uma região de
memória disponível. Um dado é empilhado na pilha apontada por r0 decrementando-
se r0 de 4 e escrevendo-se o dado na nova posição apontada por r0 (neste esquema,
Uma solução (Antiga)• Não funciona para linguagens “modernas”
–Não permite recursão
• Não funciona em sistemas operacionais “modernos”–Memória que possui código é geralmente protegida contra escrita
Como implementar recursão?
• Pilha!
• Processadores têm algumas instruções que manipulam uma pilha
• Apontador de pilha (stack pointer) é um registrador
–No Faíska, é o r15 (também chamado de sp)
• Duas operações básicas: push e pop
A pilha
• A pilha é utilizada principalmente para guardar valores temporários.
Memória do Faíska
0x0000
...
Ende
reço
Pilh
a• A pilha é armazenada na memória principal
• A pilha cresce de endereços maiores para menores.
A pilha
• O registrador stack pointer (ou r15 no Faíska) aponta para o topo da pilha. Ele deve ser inicializado antes de utilizarmos a pilha.
• Push r1SP = SP - 4[SP] = r1
• Pop r1r1 = [SP]SP = SP + 4
Memória
Principal
0x0000
SP0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
...Pi
lha
Exemplo
set r1, 0A2hset r2, 0A8CEEh; Salvar r1 e r2push r1.........
Memória
Principal
0x0000
SP
...Pi
lha 0x0500
0x04FC0x04F80x04F40x04F00x04EC0x04E8
Exemplo
set r1, 0A2hset r2, 0A8CEEh; Salvar r1 e r2push r1push r2......
SP
Memória
Principal
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
0x0000
...Pi
lha
00 00 00 A2
Memória
Principal
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
0x0000
...
Exemplo
set r1, 0A2hset r2, 0A8CEEh; Salvar r1 e r2push r1push r2......
SP
Pilh
a
00 00 00 A200 0A 8C EE
Memória
Principal
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
0x0000
...
Exemplo
set r1, 0A2hset r2, 0A8CEEh; Salvar r1 e r2push r1push r2......; Recuperar em r4 e r3pop r4
SP
Pilh
a
00 00 00 A200 0A 8C EE
Memória
Principal
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
0x0000
...
Exemplo
set r1, 0A2hset r2, 0A8CEEh; Salvar r1 e r2push r1push r2......; Recuperar em r4 e r3pop r4
SPPilh
a
00 00 00 A200 0A 8C EE
Memória
Principal
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
0x0000
...
Exemplo
set r1, 0A2hset r2, 0A8CEEh; Salvar r1 e r2push r1push r2......; Recuperar em r4 e r3pop r4pop r3
SPPilh
a
00 00 00 A200 0A 8C EE
Memória
Principal
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
0x0000
...
Exemplo
set r1, 0A2hset r2, 0A8CEEh; Salvar r1 e r2push r1push r2......; Recuperar em r4 e r3pop r4pop r3
SP
Pilh
a
00 00 00 A200 0A 8C EE
• Qual o valor de r3, r4 e r15?
Inicializando a Pilha
; Pilhafinal_pilha: ds 1024inicio_pilha:
; Antes de usarset sp, inicio_pilha...
• A pilha poderia ser inicializada para o final de um vetor alocado pelo usuário.
Inicializando a Pilha
FIM_MEM equ 10000h
; Antes de usarset sp, FIM_MEM...
• Outra forma é iniciar a pilha a partir do final da memória. A memória do Faíska tem 64 kbytes.
Procedimentos• Usam a pilha para lembrar do endereço de retorno.• Ao chamar um procedimento nós empilhamos o endereço
de retorno (o endereço da próxima instrução) na pilha.• Ao retornar de um procedimento, basta desempilharmos o
endereço de retorno da pilha e saltar para este endereço.
Procedimentos• Usam a pilha para lembrar do endereço de retorno.• Ao chamar um procedimento nós empilhamos o endereço
de retorno (o endereço da próxima instrução) na pilha.• Ao retornar de um procedimento, basta desempilharmos o
endereço de retorno da pilha e saltar para este endereço.• Instruçõescall rotulo–empilha o endereço da próxima instrução na pilha–salta para rotuloret–desempilha o endereço no topo da pilha–salta para o endereço desempilhado.
Procedimentos
Memória
SP
0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
...set r1, 0xEEpush r1call funcsub r1, r2call func...func: add r1, 1...ret...
Pilh
a...0x00100x00140x00180x00200x0024
...0x00900x00940x0098
...
IP
• CALL, RET são usados para chamar e retornar de procedimentos.
00 00 00 EE
Procedimentos
Memória
SP
...set r1, 0xEEpush r1call funcsub r1, r2call func...func: add r1, 1...ret...
Pilh
aIP
• CALL, RET são usados para chamar e retornar de procedimentos.
00 00 00 EE00 00 00 20
...0x00100x00140x00180x00200x0024
...0x00900x00940x0098
...0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
Procedimentos
Memória
SP
...set r1, 0xEEpush r1call funcsub r1, r2call func...func: add r1, 1...ret...
Pilh
a
IP
• CALL, RET são usados para chamar e retornar de procedimentos.
00 00 00 EE00 00 00 20
...0x00100x00140x00180x00200x0024
...0x00900x00940x0098
...0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
Procedimentos
Memória
SP...set r1, 0xEEpush r1call funcsub r1, r2call func...func: add r1, 1...ret...
Pilh
a
IP
• CALL, RET são usados para chamar e retornar de procedimentos.
00 00 00 EE00 00 00 20
...0x00100x00140x00180x00200x0024
...0x00900x00940x0098
...0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
Procedimentos
Memória
SP...set r1, 0xEEpush r1call funcsub r1, r2call func...func: add r1, 1...ret...
Pilh
a
IP
• CALL, RET são usados para chamar e retornar de procedimentos.
00 00 00 EE00 00 00 20
...0x00100x00140x00180x00200x0024
...0x00900x00940x0098
...0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
Procedimentos
Memória
SP
...set r1, 0xEEpush r1call funcsub r1, r2call func...func: add r1, 1...ret...
Pilh
aIP
• CALL, RET são usados para chamar e retornar de procedimentos.
00 00 00 EE00 00 00 2C
...0x00100x00140x00180x00200x0024
...0x00900x00940x0098
...0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
Procedimentos
Memória
SP
...set r1, 0xEEpush r1call funcsub r1, r2call func...func: add r1, 1...ret...
Pilh
a
IP
• CALL, RET são usados para chamar e retornar de procedimentos.
00 00 00 EE00 00 00 2C
...0x00100x00140x00180x00200x0024
...0x00900x00940x0098
...0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
Procedimentos
Memória
...set r1, 0xEEpush r1call funcsub r1, r2call func...func: add r1, 1...ret...
Pilh
a
IP
• CALL, RET são usados para chamar e retornar de procedimentos.
00 00 00 EE00 00 00 2C
...0x00100x00140x00180x00200x0024
...0x00900x00940x0098
...
SP
0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
Exemplo de Procedimento Simples
14 CAPÍTULO 4. PROCEDIMENTOS E FUNÇÕES
CALL
Chamada de procedimento
Sintaxe Operação Flags Codificação
call expr32
sp← sp + 4mem[sp]← ip + 8
ip← imd32
–
31 0
5431 0
imd32
call rdest
sp← sp + 4mem[sp]← ip + 4
ip← rdest
–31 0
55 rdest
RET
Retorno de procedimento
Sintaxe Operação Flags Codificação
retip← mem[sp]sp← sp + 4
–31 0
56
A instrução chamada de procedimento empilha o endereço de retorno na pilhae executa o desvio para o início do procedimento. Como na instrução de desvio in-condicional, o endereço alvo (início do procedimento) pode ser especificado através deum rótulo ou de um registrador. A instrução retorno de procedimento simplesmentedesempilha o endereço de retorno da pilha e executa o desvio correspondente.
Exemplo 2 Procedimento para zerar os registradores r0, r1, r2 e r3.
1 ; ZeraRegs2 ; procedimento para zerar os registradores r0, r1, r2 e r33 ; entrada: nenhuma4 ; saída: r0, r1, r2 e r3 zerados5 ; destrói: nada6 ZeraRegs:7 set r0,08 set r1,09 set r2,010 set r3,011 ret
Exemplo de Procedimento Simples
14 CAPÍTULO 4. PROCEDIMENTOS E FUNÇÕES
CALL
Chamada de procedimento
Sintaxe Operação Flags Codificação
call expr32
sp← sp + 4mem[sp]← ip + 8
ip← imd32
–
31 0
5431 0
imd32
call rdest
sp← sp + 4mem[sp]← ip + 4
ip← rdest
–31 0
55 rdest
RET
Retorno de procedimento
Sintaxe Operação Flags Codificação
retip← mem[sp]sp← sp + 4
–31 0
56
A instrução chamada de procedimento empilha o endereço de retorno na pilhae executa o desvio para o início do procedimento. Como na instrução de desvio in-condicional, o endereço alvo (início do procedimento) pode ser especificado através deum rótulo ou de um registrador. A instrução retorno de procedimento simplesmentedesempilha o endereço de retorno da pilha e executa o desvio correspondente.
Exemplo 2 Procedimento para zerar os registradores r0, r1, r2 e r3.
1 ; ZeraRegs2 ; procedimento para zerar os registradores r0, r1, r2 e r33 ; entrada: nenhuma4 ; saída: r0, r1, r2 e r3 zerados5 ; destrói: nada6 ZeraRegs:7 set r0,08 set r1,09 set r2,010 set r3,011 ret
Exemplo de chamada:
...call ZeraRegs!; após a chamada r0, r1, r2 e r3 estão zerados...
Passagem de Parâmetros
• Por Registrador–Especifica-se quais registradores devem ser usados–Podem ser especificados parâmetros de entrada ou de saída
Exemplo16 CAPÍTULO 4. PROCEDIMENTOS E FUNÇÕES
de memória, r2 contém o número de bytes a serem preenchidos com o valor dado.
1 ; Preenche2 ; procedimento para preencher com um dado byte uma região de memória3 ; entrada:4 ; r0 tem byte a ser usado no preenchimento5 ; r1 tem endereço inicial da região de memória6 ; r2 tem o número de bytes da região que devem ser preenchidos7 ; saída: nenhuma8 ; destroi: r0, r1, r2, r3 e flags910 Preenche:11 sub r2,1 ; continua a preencher?12 js final ; desvia se terminou de preencher13 stb (r1),r0 ; preenche um byte com valor dado14 add r1,1 ; avança apontador15 jmp Preenche16 final:17 ret ; e retorna quando toda região estiver preenchida
Para preencher 100 bytes a partir da posição 1000h com o valor 0ffh, podemos
utilizar o procedimento Preenche da seguinte maneira:
1 set r0,0ffh ; valor para preencher memória2 set r1,1000h ; endereço inicial3 set r2,100 ; número de bytes a preencher4 call Preenche
4.3 Passagem de parâmetros pela pilha
Apesar de eficiente, a passagem de parâmetros por registradores tem muitas limita-
ções e não pode ser utilizada em todos os casos, particularmente no caso de procedi-
mento recursivos. Em geral, a melhor maneira de passar parâmetros é utilizando a
pilha.
Para passar parâmetros utilizando a pilha, devemos empilhar os parâmetros
antes da chamada do procedimento. Dentro do procedimento, para acessar os parâ-
metros podemos utilizar o registrador apontador de pilha. Suponha que desejemos
chamar um procedimento proc com dois parâmetros param1 e param2. O código
abaixo mostra a preparação da chamada do procedimento, com os dois parâmetros
sendo empilhados antes da instrução de chamada de procedimento:
Exemplo
16 CAPÍTULO 4. PROCEDIMENTOS E FUNÇÕES
de memória, r2 contém o número de bytes a serem preenchidos com o valor dado.
1 ; Preenche2 ; procedimento para preencher com um dado byte uma região de memória3 ; entrada:4 ; r0 tem byte a ser usado no preenchimento5 ; r1 tem endereço inicial da região de memória6 ; r2 tem o número de bytes da região que devem ser preenchidos7 ; saída: nenhuma8 ; destroi: r0, r1, r2, r3 e flags910 Preenche:11 sub r2,1 ; continua a preencher?12 js final ; desvia se terminou de preencher13 stb (r1),r0 ; preenche um byte com valor dado14 add r1,1 ; avança apontador15 jmp Preenche16 final:17 ret ; e retorna quando toda região estiver preenchida
Para preencher 100 bytes a partir da posição 1000h com o valor 0ffh, podemos
utilizar o procedimento Preenche da seguinte maneira:
1 set r0,0ffh ; valor para preencher memória2 set r1,1000h ; endereço inicial3 set r2,100 ; número de bytes a preencher4 call Preenche
4.3 Passagem de parâmetros pela pilha
Apesar de eficiente, a passagem de parâmetros por registradores tem muitas limita-
ções e não pode ser utilizada em todos os casos, particularmente no caso de procedi-
mento recursivos. Em geral, a melhor maneira de passar parâmetros é utilizando a
pilha.
Para passar parâmetros utilizando a pilha, devemos empilhar os parâmetros
antes da chamada do procedimento. Dentro do procedimento, para acessar os parâ-
metros podemos utilizar o registrador apontador de pilha. Suponha que desejemos
chamar um procedimento proc com dois parâmetros param1 e param2. O código
abaixo mostra a preparação da chamada do procedimento, com os dois parâmetros
sendo empilhados antes da instrução de chamada de procedimento:
Outro Exemplo
• Escrever uma função que verifica se um caractere está presente em uma cadeia de caracteres, e retorna sua posição.–r1 tem o endereço inicial da cadeia–a cadeia termina com o byte 0–r2 tem o caractere que se deseja procurar–a função retorna em r0
• -1 se o caracter não estiver presente na cadeia •o endereço do primeiro caractere se estiver presente
Política de uso dos registradores.
• O procedimento pode utilizar muitos registradores.
• E se for especificado que o procedimento não deve alterar os valores dos registradores (a menos dos parâmetros de saída)?
• Onde armazenar?
Política de uso dos registradores.
• Na pilha!
ProcEducado: push r0 ; salva todos os registradores push r1 push r2 push r5 ... ; corpo do procedimento pop r5 ; restaura registradores pop r2 pop r1 pop r0 ret ; retorna
Política de uso dos registradores.
• Quem deve salvar os registradores:
–Quem invoca o procedimento?
–O procedimento invocado?
• Depende da aplicação
–Mas em geral é melhor deixar quem invoca salvar (para evitar salvar registradores que não precisam ser salvos)
Passagem de parâmetros pela Pilha
Uso da pilha para parâmetros
• Antes da chamada, os parâmetros são empilhados
• Dentro do procedimento, os parâmetros são lidos com o auxílio do registrador de pilha, o SP (stack pointer).
• Após a chamada, o espaço alocado para os parâmetros na pilha é desalocado.
Exemplo
Memória
SP
0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
...set r4, 0xEEset r5, 0xAApush r4push r5call funcadd sp, 8...func: ld r0, (sp+8) ld r1, (sp+4) ... ret
Pilh
a...0x00100x00140x00180x00200x00240x002c0x00900x00940x0098
...0x0102
IP
• Uso da pilha para passagem de parâmetros - Empilha o primeiro parâmetro
Exemplo
Memória
SP
0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
...set r4, 0xEEset r5, 0xAApush r4push r5call funcadd sp, 8...func: ld r0, (sp+8) ld r1, (sp+4) ... ret
Pilh
a...0x00100x00140x00180x00200x00240x002c0x00900x00940x0098
...0x0102
IP
• Uso da pilha para passagem de parâmetros - Empilha o segundo parâmetro
00 00 00 EE
Exemplo
Memória
SP
0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
...set r4, 0xEEset r5, 0xAApush r4push r5call funcadd sp, 8...func: ld r0, (sp+8) ld r1, (sp+4) ... ret
Pilh
a...0x00100x00140x00180x00200x00240x002c0x00900x00940x0098
...0x0102
IP
• Uso da pilha para passagem de parâmetros - Chama a função
00 00 00 EE00 00 00 AA
Exemplo
Memória
SP
0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
...set r4, 0xEEset r5, 0xAApush r4push r5call funcadd sp, 8...func: ld r0, (sp+8) ld r1, (sp+4) ... ret
Pilh
a...0x00100x00140x00180x00200x00240x002c0x00900x00940x0098
...0x0102
IP
• Uso da pilha para passagem de parâmetros - Lê o primeiro parâmetro
00 00 00 EE00 00 00 AA00 00 00 2c
Exemplo
Memória
SP
0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
...set r4, 0xEEset r5, 0xAApush r4push r5call funcadd sp, 8...func: ld r0, (sp+8) ld r1, (sp+4) ... ret
Pilh
a...0x00100x00140x00180x00200x00240x002c0x00900x00940x0098
...0x0102
IP
• Uso da pilha para passagem de parâmetros - Lê o segundo parâmetro
00 00 00 EE00 00 00 AA00 00 00 2c
Exemplo
Memória
SP
0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
...set r4, 0xEEset r5, 0xAApush r4push r5call funcadd sp, 8...func: ld r0, (sp+8) ld r1, (sp+4) ... ret
Pilh
a...0x00100x00140x00180x00200x00240x002c0x00900x00940x0098
...0x0102IP
• Uso da pilha para passagem de parâmetros - Retorna da função
00 00 00 EE00 00 00 AA00 00 00 2c
Exemplo
Memória
SP
0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
...set r4, 0xEEset r5, 0xAApush r4push r5call funcadd sp, 8...func: ld r0, (sp+8) ld r1, (sp+4) ... ret
Pilh
a...0x00100x00140x00180x00200x00240x002c0x00900x00940x0098
...0x0102
IP
• Uso da pilha para passagem de parâmetros - Após retornar, o espaço usado na pilha é desalocado
00 00 00 EE00 00 00 AA00 00 00 2c
Exemplo
Memória
SP
0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
...set r4, 0xEEset r5, 0xAApush r4push r5call funcadd sp, 8...func: ld r0, (sp+8) ld r1, (sp+4) ... ret
Pilh
a...0x00100x00140x00180x00200x00240x002c0x00900x00940x0098
...0x0102
IP
• Uso da pilha para passagem de parâmetros
00 00 00 EE00 00 00 AA00 00 00 2c
Uso da pilha para parâmetros
• Na linguagem C os parâmetros são empilhados na ordem inversa
TesteC(X,Y);
ld r0, Y ; empilha opush r0 ; ultimo param.ld r0, X ; empilha opush r0 ; primeiro param.call TestCadd sp, 8 ; remove da pilha
Uso da pilha para parâmetros
• Na linguagem C os parâmetros são empilhados na ordem inversa
TesteC(X,Y);
ld r0, Y ; empilha opush r0 ; ultimo param.ld r0, X ; empilha opush r0 ; primeiro param.call TestCadd sp, 8 ; remove da pilha
void TesteC(int a, int b)
TesteC: ld r1, (sp+4) ; a ld r0, (sp+8) ; b ... ret
Retorno de valor em funções
• Se é apenas um valor, podemos retornar em um registrador pré-especificado.
–Por exemplo, r0
Retorno de valor em funções4.4. RETORNO DE VALORES DE FUNÇÕES 19
1 ; ContaUm2 ; procedimento para contar o número de bits 13 ; entrada: palavra de 32 bits passada pela pilha4 ; saída: número de bits 1 em r05 ; destrói: r1, r2 e r36 ContaUm:7 ld r1,(sp+4) ; carrega parâmetro8 set r3,32 ; vamos contar 32 bits9 xor r0,r0 ; zera registrador resultado
10 proxbit:11 mov r2,r1 ; copia em rascunho12 and r2,1 ; isola bit menos significativo13 add r0,r2 ; e soma ao resultado14 ror r1,1 ; prepara para proximo bit15 sub r3,1 ; verifica se chegou ao final16 jnz proxbit ; se nao verificou todos os bits, desvia17 ret ; retorna com valor em r0
Exemplo 6 Escreva um procedimento que verifique se duas palavras de 32 bits pas-sadas como parâmetro na pilha têm o mesmo número de bits 1. Caso ambas tenhamo mesmo número, o procedimento deve retornar o bit de estado C desligado, e o re-gistrador r0 deve conter o número de bits 1. Caso contrário, o bit de estado C deveretornar ligado.
1 ; ComparaUm2 ; procedimento para comparar o número de bits 13 ; entrada: duas palavras de 32 bits passadas pela pilha4 ; saída: se iguais, número de bits 1 em r0 e flag C desligada5 ; se diferentes, flag C ligada6 ; destrói: r1 e flags7 ComparaUm:8 ld r0,(sp + 8) ; carrega primeiro parâmetro9 push r0 ; empilha como parâmetro para ContaUm
10 call ContaUm ; conta o número de bits 1 do primeiro11 add sp,4 ; retira parametro da pilha12 mov r1,r0 ; guarda resultado intermediario em registrador13 ld r0,(sp + 4) ; carrega segundo parâmetro14 push r0 ; empilha como parâmetro para ContaUm15 call ContaUm ; conta o número de bits 1 do segundo16 add sp,4 ; retira parametro da pilha17 cmp r0,r1 ; compara resultados18 jz final ; se iguais, pode retornar (e C já está19 ; com o valor correto)20 stc ; caso contrário, liga bit de estado C21 ; (se r0 > r1 ainda estava desligado)22 final:23 ret ; retorna com valor em r0
Passagem de parâmetros por Valor
• Suponha a função C
int ContaUm(int v){ ...}
• Exemplo de chamada
int x,y;...y = ContaUm(x);
Passagem de parâmetros por Valor
• Suponha a função C; Conta o numero de bits 1; entrada: palavra de 32 bits pela pilha; saida: numero de bits 1 em r0; destroi: r1, r2, r3 e r4ContaUm:! ld r1,(sp+4)! ; carrega parametro em r1! set r0, 0h! ; inicia r0 com 0.loop:! set r2, 1h! ; mascara para teste! and r2, r1! ; isola bit menos significativo! add r0, r2! ; soma 1 se o bit estiver setado! shr r1, 1! ; desloca bits para direita! cmp r1, 0! ; verifica se não tem mais bits 1! jnz loop ; retorna para o laço se houver! ret
Passagem de parâmetros por Valor
• y = ContaUm(x);
org 1000h...y: ds 4x: ds 4...! ld r0, x! ; empilha o VALOR do parâmetro! push r0! call ContaUm! add sp, 4! st y, r0! ; guarda o valor de retorno em y
Passagem de parâmetros por Referência
• Considere o procedimento:void troca(int *a, int* b){! int x;! x = *a;! *a = *b;! *b = x;}
• Exemplo de chamada:int x, y;...troca(&x, &y);
Passagem de parâmetros por Referência
• troca(&x, &y);org 1000h...y: ds 4x: ds 4...! set r0, y! ; empilha o ENDEREÇO de y! push r0! set r0, x! ; empilha o ENDEREÇO de x! push r0! call troca! add sp, 8! ; remove parâmetros da pilha
Implementação de Troca
void troca(int *a, int* b){ int x; x = *a; *a = *b; *b = x;}
Implementação de Troca
void troca(int *a, int* b){ int x; x = *a; *a = *b; *b = x;}
; troca: troca dois valores; entrada: endereços na pilha; destrói: r0, r1, r2, r3 e r4troca:! ld r0, (sp+4) ; &a! ld r1, (sp+8) ; &b! ld r2, (r0) ; a! ld r3, (r1) ; b! mov r4, r2 ; x = a! st (r0), r3 ; *a = b! st (r1), r4 ; *b = x! ret
Se quizer preservar registradores: Pilha!
org 1000h...y: ds 4x: ds 4...! push r1! ; preserva r1! push r2! ; preserva r2! ld r0, x! ; empilha parametro!! push r0! call ContaUm! add sp, 4! ; remove parametro da pilha! pop r2! ; restaura r2! pop r1! ; restaura r1
Variáveis Locais
• Quando um procedimento utiliza muitas variáveis locais, e o número de registradores não é suficiente para alocá-las, a pilha também é utilizada para armazenar estas variáveis locais.
• O espaço para as variáveis locais é reservado na entrada do procedimento, e desalocado ao final do procedimento.
• Mas isso altera o valor de SP, precisamos ter cuidado ao acessar os parâmetros
Variáveis Locais
• Utilizamos outro registrador para facilitar acesso a parâmetros e variáveis locais.
• Frame Pointer (FP): apontador de quadro
• No Faíska, FP é sinônimo de r14
• FP marca a posição de SP na entrada do procedimento
–Estabelece um ponto fixo de acesso
Exemplo
Memória
SP
0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
var. local 1var. local 2var. local 3
Pilh
aparam 2param 1retorno
FP
0x04E4
Variáveis Locais
• Na entrada do procedimento, salva (empilha) o valor anterior de FP e faz FP apontar para o valor corrente de SP (topo da pilha).
ContaUm:! push fp ; salva fp! mov fp, sp ; faz fp apontar para sp! ...! pop fp ; restaura fp! ret ; retorna
• Antes de retornar, restaura o valor antigo de FP
Exemplo
Memória
SP
0x0000
0x05000x04FC0x04F80x04F40x04F00x04EC0x04E8
var. local 1var. local 2var. local 3
Pilh
aparam 2param 1retorno
FP
0x04E4
FP salvo
Variáveis Locais
• Acesso a parâmetros e variáveis locais usa FP
um_proc:! push fp! mov fp, sp! sub sp, 8 ; espaço para 2 ! ; variáveis locais! ...! ld r0, (fp+8) ; parâmetro 1! ld r1, (fp+12) ; parâmetro 2! ...! ld r2, (fp-4) ; var. local 1! ld r3, (fp-8) ; var. local 2
Representação de Registros na Memória
struct id {! int cpf;! char nome[256];! int idade;};
- Como representar?- Como acessar os campos?
Representação de Registros na Memória
struct no {! struct no* dir;! struct no* esq;! int chave;};
struct no x;x.dir;x.esq;x.chave;