EA869 Subrotinas Faculdade de Engenharia Elétrica e de Computação (FEEC) Universidade Estadual de Campinas (UNICAMP) Prof. Levy Boccato 1 Objetivos Definir o conceito de subrotina e sua importância na implementação de código em linguagem de montagem. Conhecer as estratégias para a correta preparação de subrotinas. Conhecer de que modo são passados parâmetros à subrotina. 2 Exemplo Considere o exemplo abaixo em linguagem Assembly fictícia, onde o OP1 é o registrador destino: MOV R0, #100 MOV R1, (R0)+ MOV R2, (R0)+ ADD R3, R1, R2 MOV (R0), R3 MOV R0, #200 MOV R1, (R0)+ MOV R2, (R0)+ ADD R3, R1, R2 MOV (R0), R3 MOV R0, #300 MOV R1, (R0)+ MOV R2, (R0)+ ADD R3, R1, R2 MOV (R0), R3 O programa faz somas de posições contíguas da memória e armazena o resultado no último endereço. Necessita de 15 linhas de memória. O que poderíamos fazer para otimizar a escrita deste programa? 3 Exemplo Observe que parte do código se repete ao longo do programa. MOV R0, #100 MOV R1, (R0)+ MOV R2, (R0)+ ADD R3, R1, R2 MOV (R0), R3 MOV R0, #200 MOV R1, (R0)+ MOV R2, (R0)+ ADD R3, R1, R2 MOV (R0), R3 MOV R0, #300 MOV R1, (R0)+ MOV R2, (R0)+ ADD R3, R1, R2 MOV (R0), R3 4 Exemplo Observe que parte do código se repete ao longo do Criar um subprograma ou uma subrotina! MOV R0, #100 MOV R1, (R0)+ MOV R2, (R0)+ ADD R3, R1, R2 MOV (R0), R3 MOV R0, #200 MOV R1, (R0)+ MOV R2, (R0)+ ADD R3, R1, R2 MOV (R0), R3 MOV R0, #300 MOV R1, (R0)+ MOV R2, (R0)+ ADD R3, R1, R2 MOV (R0), R3 O que poderíamos fazer para resolver essa redundância? programa. Label identifica a posição de memória onde se inicia a subrotina. SUB: MOV R1, (R0)+ MOV R2, (R0)+ ADD R3, R1, R2 MOV (R0), R3 RTS Instrução que informa o fim da subrotina. 5 Exemplo Observe que parte do código se repete ao longo do Criar um subprograma ou uma subrotina! PRINC: MOV R0, #100 CALL SUB MOV R0, #200 CALL SUB MOV R0, #300 CALL SUB MOV R0, #200 Instrução que chama a subrotina O que poderíamos fazer para resolver essa redundância? programa. Label identifica a posição de memória onde se inicia a subrotina. SUB: MOV R1, (R0)+ MOV R2, (R0)+ ADD R3, R1, R2 MOV (R0), R3 RTS Instrução que informa o fim da subrotina. MOV R0, #300 6 Exemplo Observe que parte do código se repete ao longo do Criar um subprograma ou uma subrotina! PRINC: MOV R0, #100 CALL SUB MOV R0, #200 CALL SUB MOV R0, #300 CALL SUB Instrução que chama a subrotina O que poderíamos fazer para resolver essa redundância? programa. Label identifica a posição de memória onde se inicia a subrotina. SUB: MOV R1, (R0)+ MOV R2, (R0)+ ADD R3, R1, R2 MOV (R0), R3 RTS Instrução que informa o fim da subrotina. O programa que antes ocupava 15 linhas de memória, agora necessita apenas de 6. Além disso, a subrotina poderá ser aproveitada em outras partes do programa, tornando o código que efetivamente é armazenado mais enxuto. Conceito de modularização 7 Subrotinas Definição: Subrotinas permitem que uma mesma sequência de instruções seja executada em diferentes pontos de um programa, sobre diferentes conjuntos de dados. O fluxo de execução do programa será desviado para a posição de memória onde a subrotina está armazenada; após sua execução, o controle deverá ser retornado ao ponto imediatamente posterior ao da ocorrência do desvio. 8 Subrotinas Vantagens: Redução de código duplicado em um programa. Possibilidade de reutilizar o mesmo código sem grandes alterações em outros programas. Decomposição de problemas grandes em pequenas partes – recurso bastante comum em linguagens de alto nível. Melhorar a interpretação visual de um programa. Esconder ou regular uma parte de um programa, mantendo o restante do código alheio às questões internas que são resolvidas ou implementadas dentro da subrotina. 9 Voltando ao exemplo 10 SUB: MOV R1, (R0)+ 11 MOV R2, (R0)+ 12 ADD R3, R1, R2 13 MOV (R0), R3 14 RTS 1 PRINC: MOV R0, #100 2 CALL SUB 3 MOV R0, #200 4 CALL SUB 5 MOV R0, #300 6 CALL SUB 7 MOV R0, #200 Vamos executá-lo! CALL SUB Desvio para Subrotina • Guardar o endereço de retorno • Desviar a execução do programa para a Subrotina PC 12 13 10 14 12 11 End-R 3 RTS Retorno para o programa • Desviar a execução para o endereço correto (End-R) Como implementar as instruções CALL e RTS? 10 Implementando uma subrotina • Vamos definir algumas estratégias para implementar microoperações envolvidas nas instruções CALL e RTS. • Voltando para nosso programa inicial, mas agora simplificado: subrotinas, determinando as PC 10 SUB: MOV R1, (R0)+ 11 MOV R2, (R0)+ 12 ADD R3, R1, R2 13 MOV (R0), R3 14 RTS 1 PRINC: MOV R0, #100 2 CALL SUB 3 MOV R0, #200 32 10 14 RL 3 ESTRATÉGIA 1: REGISTRADOR LIGANTE (RL) O endereço de retorno é armazenado em um registrador especial (RL) CALL SUB RTS 1. Armazena o endereço de retorno em RL: RL ← (PC) 2. Desvia a execução para a subrotina: PC ← mem SUB Endereço seguinte à instrução CALL 1. Desviar a execução do programa para endereço contido em RL PC ← (RL) 11 Implementando uma subrotina 1 PRINC: MOV R0, #100 2 CALL SUB 3 MOV R0, #200 10 SUB: MOV R1, (R0)+ 11 MOV R2, (R0)+ 12 ADD R3, R1, R2 13 MOV (R0), R3 14 RTS ESTRATÉGIA 2: ENDEREÇO DE RETORNO JUNTO À SUBROTINA O endereço de retorno é armazenado na primeira linha da subrotina Como? Reservando a palavra de memória referente a primeira linha da subrotina: pseudo-instrução DS 12 Implementando uma subrotina 1 2 3 PRINC: MOV R0, #100 CALL SUB MOV R0, #200 10 SUB: DS 1 MOV R1, (R0)+ 11 MOV R2, (R0)+ 12 ADD R3, R1, R2 13 MOV (R0), R3 14 RTS 15 ESTRATÉGIA 2: ENDEREÇO DE RETORNO JUNTO À SUBROTINA O endereço de retorno é armazenado na primeira linha da subrotina Reservando a palavra de memória referente a primeira linha da subrotina: pseudo-instrução DS Como? CALL SUB RTS 1. Endereço de retorno é armazenado no endereço SUB SUB ← (PC) 2. Desvia a execução para a subrotina: PC ← end-de-SUB Endereço seguinte à instrução CALL 1. Desviar a execução do programa para endereço contido em SUB PC ← (SUB) 13 Implementando uma subrotina 1 2 3 PRINC: MOV R0, #100 CALL SUB MOV R0, #200 10 SUB: DS 1 MOV R1, (R0)+ 11 MOV R2, (R0)+ 12 ADD R3, R1, R2 13 MOV (R0), R3 14 RTS 15 ESTRATÉGIA 2: ENDEREÇO DE RETORNO JUNTO À SUBROTINA Qual é a limitação destas duas formas de implementação? Para exemplificar, vamos analisar um exemplo real com o processador ARM... 14 Exemplo: ARM SUBROTINA 1 PROG. PRINC SUBROTINA 2 11 MOV R1,R2 20 SUB1: ... 40 SUB2: ... 12 BL SUB1 21 BL SUB2 41 ... 13 ... 22 ... 42 BX LR 23 BX LR 43 14 NÃO é possível chamar uma subrotina estando dentro de outra subrotina! R14(LR) Como solucionar? 13 22 Uso da Pilha! 15 Implementando uma subrotina 1 PRINC: MOV R0, #100 2 CALL SUB 3 MOV R0, #200 10 SUB: MOV R1, (R0)+ CALL SUB2 11 RTS 12 14 SUB2:MOV R1, R3 RTS 15 ESTRATÉGIA 3: ENDEREÇO DE RETORNO ARMAZENADO NA PILHA O endereço de retorno é armazenado na última posição livre da pilha. Desta forma, pode-se chamar subrotinas recursivamente 16 Implementando uma subrotina 1 PRINC: MOV R0, #100 2 CALL SUB 3 MOV R0, #200 10 SUB: MOV R1, (R0)+ CALL SUB2 11 RTS 12 14 SUB2:MOV R1, R3 RTS 15 12 3 PILHA ESTRATÉGIA 3: ENDEREÇO DE RETORNO ARMAZENADO NA PILHA O endereço de retorno é armazenado na última posição livre da pilha. Desta forma, pode-se chamar subrotinas recursivamente CALL SUB 1. Endereço de retorno é armazenado na pilha (SP) ← (PC) SP ← (SP) + 1 RTS 1. Endereço de retorno é retirado da pilha e alocado em PC SP ← (SP) - 1 PC ← ((SP)) 2. Desvia a execução para a subrotina: PC ← end-de-SUB 17 Subrotinas Agora que já sabemos estratégias de como implementar uma subrotina usando as instruções CALL e RTS, precisamos abordar a questão de como os dados são passados para a subrotina. • Ao chamar uma subrotina através da instrução CALL, é necessário informar onde estão os dados que ela irá manipular. Estes dados formam os parâmetros reais. • Ao declarar uma subrotina, deve-se indicar quais variáveis ela irá manipular. Estas variáveis são conhecidas como parâmetros formais. Toda vez que a subrotina é chamada, estes parâmetros são substituídos pelos reais, enviados para a subrotina. MOV R0, #100 CALL SUB (A,B) MOV R0, #200 SUBROTINA SUB (X,Y) X = Y*3 Y=3+X Finalmente, como os dados são passados para a subrotina? 18 Passagem de parâmetros • Considere que os dados que a subrotina irá manipular estejam na memória. • Existem duas formas de informá-los à subrotina: Por Valor Por Referência Passo para a subrotina uma cópia do dado Passo para a subrotina o endereço do dado Como? Passando o conteúdo do endereço Implicações Toda vez que a subrotina manipula um dado, ela está alterando uma cópia dele, mantendo inalterado o dado original. Como? Informando o endereço #label Implicações Desta forma, toda vez que a subrotina manipula um dado, ela está alterando-o permanentemente. Mas como os dados são alocados na memória? 19 Passagem de parâmetros Como os dados são alocados na memória? Basicamente há duas formas de alocar dados na memória: 1. Através de periféricos: Mouse, teclado, tela touchscreen, HD, enviam dados para os processadores. Estes dados são alocados, primeiramente, na memória e ficam disponíveis para serem acessados pelos programas. 2. Através de pseudo-instruções: É um tipo especial de instrução (veremos mais adiante), não executável, que insere valores em posições de memória ou reserva espaço para receber valores: DS X Reserva X palavras da memória a partir da posição da pseudo-instrução. DW X Posição de memória desta pseudoinstrução recebe o valor X. 20 Passagem de parâmetros • Vamos analisar mais detalhadamente como se procede a passagem de parâmetros por valor e por referência: DADO: DW 200 RESULT: DS 1 MOV DADO , R1 MOV #RESULT , R3 CALL SUB DADO: DW 200 RESULT: DS 1 MOV #DADO , R1 CALL SUB MOV R2, RESULT valor referência referência SUB: ... MOV R1 , (R3) ... RTS SUB: ... MOV (R1),R2 ... ADD #10, R2 RTS 21 Passagem de parâmetros • Vamos identificar a passagem de parâmetros no programa abaixo e verificar, passo a passo, as posições de memória dos dados de entrada e saída: PROGRAMA SUBROTINA PASSAGEM DE PARÂMETROS MOVE DAD1, DIN1 MOVE DAD2, DIN2 CALL SUB MOVE RES, RESU STOP DAD1: DW 5 DAD2: DW 18 RESU: DS 1 valor valor DIN1: DS 1 DIN2: DS 1 RES: DS 1 SUB: ADD DIN1, DIN2 MOVE DIN2, RES RTS INICIALIZAÇÃO DOS DADOS 22 Passagem de parâmetros • (1) (2) (3) (4) (5) (6) Considere que a chamada da subrotina foi implementada com pilha, e que DADO está na posição 20 de memória. Cada instrução ocupa uma palavra. MOV #0,SP MOV #DADO,R1 PUSH R1 CALL SUB POP R1 MOV R1 , RES STOP DADO: DW 50 RES: DS 1 ... (100)SUB: POP R3 POP R2 (101) MOV (R2),R5 (102) SUB #3 , R5 (103) PUSH R5 (104) PUSH R3 (105) RTS (106) PC ← 5 Inic. (1) (2) (3) (4) (100) (101) (102) (103) (104) (105) (106) (5) (6) DADO RES R1 R2 R3 R5 SP 0 1 50 50 50 X X X X X 20 X X X X X X X X X X 0 X X X X X X 50 50 X X 20 X X X X X 20 X 1 2 20 X 5 50 50 50 50 50 X 20 20 20 20 20 X X 1 20 20 20 20 5 5 5 5 5 X 50 47 47 0 0 0 1 20 20 20 20 47 5 5 5 5 5 20 20 47 47 20 20 20 20 5 5 5 5 47 47 47 47 2 1 0 47 47 47 0 47 5 X X X 50 50 50 50 X X X X X X X 47 20 0 23 Conclusão Uma forma eficiente de escrever um programa é através da modularização de partes repetidas do código. Estes módulos podem ser acessados pelo programa em diferentes momentos e com diferentes dados de entrada – são conhecidos como subrotinas. A subrotina é chamada por uma instrução especial (CALL) e sua execução termina com a instrução RTS. As instruções CALL e RTS podem ser implementadas através de 3 estratégias: via registrador RL, via endereço de retorno na primeira linha da subrotina e via pilha. Os dados podem ser passados para a subrotina de duas formas (passagem de parâmetros): por valor, onde passo uma cópia do dado e por referência, onde passo o endereço do dado. O uso da pilha é de extrema importância nas chamadas de subrotina, já que permite que elas chamem outras subrotinas (ou elas próprias, num esquema recursivo). Tal fato faz seu uso ser bastante comum nos processadores. 24 Créditos Este material está baseado nas notas de aula elaboradas pelo Prof. Léo Pini e pelo aluno de doutorado Tiago Novaes. 25