Linguagem de Montagem Programa em Linguagem de Alto Nível C, C++, PASCAL, FORTRAN, COBOL, etc. Compilador Programa em Linguagem de Montagem Montador Segue Objeto: Programa em Linguagem de Máquina x86, MIPS, SPARC, etc. 1 Objeto: Rotinas da Biblioteca (em linguagem de máquina) Objeto: Programa em Linguagem de Máquina Ligador Carregador Programa Executável Memória Código em Linguagem Alto Nível main () { int a,b,c; } a=3; b=4; c=a+b; if (c==7) printf ("A soma deu certo! \n"); else printf ("A soma deu errado! \n"); exit(0); 2 Resultado da Compilação ;; Compilado por Gabriel ;; Área de Dados . data . align 2 String1: . ascii "A soma deu certo! \n\000" . align 2 String2: . asciiz "A soma deu errado! \n" . align 2 Certo: . word String1 . align 2 Errado: . word String2 Valor: . space 4 ;; Área de Código .text .align 2 .global Main Main: addi r2, r0, #3 addi r3, r0, #4 add r4, r2, r3 addi r1, r0, #7 sub r5, r4, r1 bnez r5, Else Then: sw Valor, r0 addi r14, r0, Certo trap #5 j Fim Else: sw Valor, r0 addi r14, r0, Errado trap #5 Fim: trap #0 3 Modelo de Programação O modelo de programação da arquitetura DLX que estaremos utilizando em nosso estudo é composta por: 32 registradores de uso geral, nomeados de R0 a R31. Esses registradores são referenciados explicitamente pelas instruções que, normalmente, possuem dois registradores como fonte e um registrador como destino das operações. Um registrador apontador de instruções (PC), é referenciado implicitamente pelas instruções de desvio, chamada e retorno de procedimento. Ele contém o endereço de memória da próxima instrução a ser executada. Modelo de Programação SEMPRE 0 R0 R16 R1 R17 R2 R18 R3 R19 R4 R20 R5 R21 R6 R7 32 bits R22 R23 R8 R24 R9 R25 R10 R26 R11 R27 R12 R28 R13 R29 R14 R30 R15 R31 32 bits Apontador de Instruções (PC) 32 bits 4 Classificação das Instruções •Instruções Aritméticas e Lógicas (Inteiras) • Operações de soma, subtração, deslocamento, e lógico, ou lógico, multiplicação, divisão envolvendo inteiros. • Instruções de Transferência de Dados • Leitura de dados armazenados na memória para os registradores e escrita do conteúdo dos registradores na memória. • Instruções de Transferência de Controle • Instruções que alteram o valor do PC: desvio condicional e incondicional, chamada de procedimento. • Instruções de Ponto Flututante • Utilizam dados em ponto flutuante como operandos. Instruções Aritméticas e Lógicas (Inteiras) nop and sll addi sub srli sge slti ; “no operation” R1,R2,R3 ; R1 = R2 and R3 R3,R4,R5 ; R3 = R4 << R5 R15, R0, #25; R15 = 25 R6, R7,R8 ; R6 = R7 – R8 R9,R10, #5 ; R9 = R10 >> 5 R1, R4, R6 ; Se R4 >= R6 Æ R1 = 1 R5, R8, #3 ; Se R8 < 3 Æ R5 = 1 ; A comparação pode ser: ; SEQ (==),SNE (!=), ; SLT (<), SGT(>), ; SLE (<=) ou SGE (>=) • Realizam operações aritméticas e lógicas entre dois registradores ou entre um registrador e um operando imediato, guardando o resultado em um terceiro registrador. 5 Instruções de Comparação Maior ou igual: sge R1, R2, R3 Maior: sgt R1, R2, R3 Menor ou igual: sle R1, R2, R3 Menor: slt R1, R2, R3 Igual: seq R1, R2, R3 Diferente: sne R1, R2, R3 ;; Se R2 >= R3 então R1 =1 ;; Se R2 > R3 então R1 =1 ;; Se R2 <= R3 então R1 =1 ;; Se R2 < R3 então R1 =1 ;; Se R2 == R3 então R1 =1 ;; Se R2 != R3 então R1 =1 Transferência de dados Realizam a transferência do conteúdo de uma posição de memória para um registrador (“load”) e vice-versa (“store”). A quantidade de bytes transferidos é dada pelo tipo de instrução: lb e sb – transferem apenas 1 byte. lw e sw – transferem 4 bytes. Formato básico: lw R5, endereço Î lb R9, endereço Î sw endereço, R6 Î sb endereço, R8 Î R5 = MEM[endereço] (lê 4 bytes) R9= MEM[endereço] (lê 1 byte) MEM[endereço] = R6 (escreve 4 bytes) MEM[endereço] = R8 (escreve 1 byte) 6 Transferência de dados O valor do endereço de memória a ser lido ou escrito é obtido pela soma do conteúdo de um registrador com um dado imediato. O dado imediato pode ser um rótulo representando um endereço de memória: lw R5, 0(R16) Î R5 = Memória[R16 + 0] lb R9, 150(R0) Î R9 = Memória [150] sw 200(R0), R6 Î Memória[200] = R6 sb 8(R16), R8 Î Memória[R16+8] = R8 lw R8, A(R0) Î R8 = Memória[A] Transferência de Controle a) Desvios Incondicionais j 1000 Î PC = 1000 b) Desvios Condicionais bnez R1, 2000 Î se R1 != 0 Æ PC=2000 beqz R8, 3000 Î se R8 == 0 Æ PC= 3000 c) Chamada e Retorno de Procedimento jal jr rotina Î R31 = PC+4, PC = rotina R31 Î PC = R31 7 Transferência de Controle Chamadas ao sistema trap #imed trap #0 trap #5 Desvia para o sistema operacional para executar um serviço especificado pelo dado imediato #imed. Muda o modo de execução do processador de normal para privilegiado Instruções de Ponto Flutuante Além das instruções que manipulam valores inteiros, os processadores necessitam ter instruções específicas para operar com valores reais. Normalmente esses dados estão no formato IEEE 754, em precisão simples (32 bits) ou precisão dupla (64 bits). Este padrão foi dotado por quase todos os computadores a partir de 1980. Essas instruções são chamadas de instruções de ponto flutuante. As instruções de ponto flutuante fazem uso, normalmente, de registradores especiais, que possuem 64 ou mais bits, denominados de registradores de ponto flutuante, de onde são lidos/escritos os operandos dessas instruções. 8 Instruções de Ponto Flutuante O principal motivo para a separação das instruções em inteiras e de ponto flutuante, é que as últimas necessitam de circuitos muito mais complexos para realizarem as operações aritméticas, o que resulta em um tempo muito maior de execução. Como os números inteiros representam uma classe muito grande dos problemas resolvidos por computador, foram criadas duas classes de instruções, para que as instruções inteiras pudessem ser executadas mais rapidamente. Exemplos a) A = 10; Hipótese: A variável A está armazenada no endereço de memória 100 e armazena 1 byte. addi R8, R0, #10 ;; registrador R8 recebe 10 sb 100(R0), R8 ;; A recebe o conteúdo de R8 ;; que é 10 9 Exemplos b) A = B + 2; Hipótese: As variáveis inteiras A e B estão armazenadas nos endereços de memória definidos pelos rótulos A e B. Cada inteiro armazena 4 bytes. lw R8, B(R0) addi R8, R8, #2 sw A(R0), R8 ;; registrador R8 recebe o ;; conteúdo de B ;; R8 = B + 2 ;; A recebe o conteúdo de R8 ;; que é B + 2 Exemplos c) A = (B + C) – (D + E) ; Hipótese: As variáveis inteiras A, B, C, D e E estão armazenadas nos registradores R1, R2, R3, R4 e R5. add R8, R2, R3 add R9, R4, R5 sub R1, R8, R9 ;; registrador R8 recebe B + C ;; registrador R9 recebe D + E ;; A recebe R8 – R9 ;; que é (B+C) – (D+E) 10 Exemplos d) C = B + A[8]; Hipótese: variáveis B e C armazenadas na memória nos endereços definidos pelos rótulos B e C. O vetor A possui o primeiro elemento armazenado do endereço 100 de memória. Todas as variáveis armazenam 1 byte cada. addi lb lb add sb R16, R0, #100 R8, 8(R16) R9, B(R0) R10, R8, R9 C(R0), R10 ;; R16 recebe o endereço de A[0] ;; registrador R8 recebe A[8] ;; registrador R9 recebe B ;; R10 = B + A[8] ;; C = R10 Exemplos e) A[12] = B + A[8]; Hipótese: variável B está armazenada na memória. O vetor A tem o seu endereço inicial definido pelo rótulo A. Todas as variáveis armazenam 4 bytes. addi lw lw add sw R16, R0, A R8, 32(R16) R9, B(R0) R8, R9, R8 48(R16), R8 ;; R16 = endereço de A[0] ;; R8 = A[8] ;; R9 = B ;; R8 = B + A[8] ;; A[12] = R8 11 Exemplos f) C = B + A[i]; Hipótese: O vetor A e as variáveis B, C estão armazenadas em memória. A variável i está armazenada em R1. Todas as variáveis armazenam 4 bytes. addi add add add lw lw add sw R16, R0, A R9, R1, R1 R9, R9, R9 R17, R16, R9 R8, 0(R17) R7, B(R0) R9, R8, R7 C(R0), R9 ;; R16 = endereço de A[0] ;; R9 = i + i ;; R9 = 2i + 2i (R9 = 4i) ;; R17 = endereço de A[i] ;; R8 = A[i] ;; R7 = B ;; R9 = A[i] + B ;; C = R9 Exemplos g) if (A = = B) { A = B + C; } D = D - B; Hipótese: variáveis de A até D estão armazenadas nos registradores de R1 até R4, respectivamente. L1: sub bnez add sub R8, R1, R2 R8, L1 R1, R2, R3 R4, R4, R2 ;; R8 = A - B ;; Se R8 != 0 então PC = L1 ;; Se A = = B executa ;; D = D – B (é sempre ;; executado) 12 Exemplos h) if (A = = B) D = B + C; else D = C – B; Hipótese: a mesma do exemplo anterior seq beqz add j ELSE: sub CONT: nop R8, R1, R2 R8, ELSE R4, R2, R3 CONT R4, R3, R2 ;; Se A = = B então R8 = 1 ;; desvia para ELSE se A ! = B ;; não executa se A ! = B ;; tem que pular o ELSE ;; não executa se A = = B Exemplos i) i = 0; do { A = A +( B[i]/2); i = i +1; } while ( i < 10 ); Hipótese: A variável i está armazenada no registrador R1 e a variável A e o vetor B estão armazenados em memória. Todas as variáveis armazenam 4 bytes. 13 Exemplos i) addi addi LACO: slli add lw srli lw add sw addi slti bnez R16, R0, B ;; R16 = endereço de B[0] R1, R0, R0 R8, R1, #2 R17, R16, R8 R8, 0(R17) R8, R8, #1 R9, A(R0) R8, R8, R9 A(R0), R8 R1, R1, #1 R8, R1, #10 R8, LACO ;; i = 0 ;; R8 = 4i ;; R17 = endereço de B[i] ;; R8 = B[i] ;; R8 = B[i] /2 ;; R9 = A ;; R8 = A + B[i]/2 ;; A = R8 ;; i = i +1 ;; Se R1 < 10 então R8 = 1 ;; Se R8 = = 1 então PC = LACO Exercício j) for ( i = 0; i < 10; i++) { A = A + B[i]/2; } Dadas as mesmas condições do exercício anterior, faça a tradução do código em alto nível para linguagem de montagem. 14 Exemplos k) A seguir apresentamos o exemplo de uma pequena rotina em “C” que copia caracteres de uma cadeia para outra até encontrar um caractere nulo ( \0 ): void copia (char *x, char *y) { while (*x != 0) { *y = *x; x++; y++; } } Exemplos k) Hipóteses: os endereços iniciais das cadeias x e y estão em R1 e R2, respectivamente. copia: lb beqz laço: sb addi addi lb bnez fim: jr R31 R6, 0(R1) R6, fim 0(R2), R6 R1, R1, #1 R2, R2, #1 R6, 0(R1) R6, laço ;; R1 = *x ( conteúdo da memória ) ;; se *x == \0 então termina ;; *y = R6 ( faz a cópia ) ;; x++ ( aponta para a posição seguinte ) ;; y++ ( da cadeia de origem e destino) ;; R6 =*x (lê um novo caractere) ;; desvia se diferente de \0 ;; retorna do procedimento 15 Formato das Instruções O processador só entende as instruções em linguagem de máquina, isto é, no formato binário. Logo, todas as informações que estão na linguagem de montagem devem estar codificadas na instrução em linguagem de máquina, que no DLX possuem todas 32 bits. Nestes 32 bits, de acordo com o tipo da instrução, podem ser encontradas os seguintes campos: opcode: O código da operação (6 bits) rs1: Registrador com o primeiro operando fonte (5 bits) rs2: Registrador com o segundo operando fonte (5 bits) rd: Registrador que guarda resultado da operação (5 bits) imediato: Valor utilizado como dado imediato (16 bits) função: Especifica uma variação da instrução básica. desloc.: Uma constante de 26 bits para ser adicionada com sinal ao valor atual do PC. Formato das Instruções As instruções no DLX podem ser de 3 tipos: Instruções do tipo I Tipicamente as que possuem dados imediatos. Instruções do tipo R As aritméticas com 3 operandos são um exemplo. Instruções do tipo J As instruções de desvio incondicional se incluem neste grupo. Cada um destes tipos possui um formato diferente, com campos distintos, para atender às funções que cada uma realiza. 16 Instruções do Tipo I Loads e stores: lw, sw, lb e sb. Instruções com dado imediato: addi, subi, etc. Instruções de desvio condicional: beqz, bnez. Instruções de desvio indireto: jr. Instruções do Tipo R Instruções lógicas e aritméticas de registrador para registrador: add, sub, sll, srl, etc. 17 Instruções do Tipo J Instruções de desvio incondicional: J (jump) e JAL (jump and link) Instruções de chamada ao sistema: TRAP Formato das Instruções - Exemplos add R1, R2, R3 000000 00010 00011 00001 00000001000 00110 00100 00000010010 and R4, R5, R6 000000 00101 addi R8, R0, #10 001000 00000 01000 0000000000001010 18 Formato das Instruções – Exemplos lw R1, 10(R5) 100011 00101 00001 0000000000001010 00000 0000100000000000 bnez R8, 2048 000101 01000 j 1024 000010 00000000000000010000000000 Modos de Endereçamento Endereçamento imediato: O operando (dado) está codificado na própria instrução. addi R3, R4, #20 Endereçamento de registrador: O dado está contido no registrador especificado. sub R4, R5, R6 19 Modos de Endereçamento Endereçamento indireto: O registrador especificado na instrução contém o endereço de memória do dado. Pode ser acompanhado de uma constante de deslocamento ou não, que é somada ao valor do registrador, para calcular o endereço do dado na memória. lw sw R4, 8(R6) 100(R0), R5 Modos de Endereçamento Endereçamento indexado: Além de um registrador contendo o endereço base, um outro registrador é utilizado como índice, sendo somado ao valor do registrador, para calcular o endereço de memória. Isto é utilizado, por exemplo, quando queremos endereçar os elementos de um vetor ou matriz. Este modo de endereçamento não existe no DLX. 20 Modos de Endereçamento Endereçamento de relativo ao PC: Neste modo de endereçamento, a instrução utiliza o valor atual do apontador de instruções, para adicionar ou subtraílo de um determinado valor, guardado na própria instrução ou armazenado em um registrador. j jr jal 2000 R31 rotina Modos de Endereçamento Endereçamento de pilha: Uma pilha consiste em itens de dados armazenados em ordem consecutiva na memória, que normalmente cresce do “final” da memória para o início. O primeiro item é denominado “fundo da pilha” e o último “topo da pilha”. São permitidas apenas operações de “pop” e “push” para retirar ou colocar dados da pilha, respectivamente. Este modo de endereçamento não existe no DLX. 21 Chamadas de Procedimento As rotinas ou procedimentos são trechos idênticos de código que, ao invés de serem replicados diversas vezes ao longo de um programa, são agrupados em uma única cópia em um endereço fixo de memória para realizar uma função específica. Em cada trecho do programa onde precisam ser utilizados é feito um desvio para este endereço fixo, sendo que os parâmetros necessários para a execução da rotina são passados em registradores ou em uma estrutura em memória. Normalmente, as instruções de desvio são usadas para mudar a execução de um ponto para outro do programa. Chamadas de Procedimento As rotinas, entretanto, não podem ser chamadas desta maneira, pois ao seu término devem retornar para pontos diferentes do programa principal, o que não seria possível apenas com as instruções comuns de desvio. A solução utilizada no DLX foi o uso de duas novas instruções: uma (jal) que guarda em um registrador, antes de realizar o desvio, o endereço da instrução seguinte àquela que chamou a rotina. Na arquitetura DLX esse endereço é salvo no registrador R31, que recebe o nome especial de RA (return address). 22 Chamadas de Procedimento A outra instrução utilizada é o desvio indireto (jr) que altera o conteúdo do PC de acordo com o valor que está armazenado em um registrador. Assim, ao final da execução do código da rotina, uma instrução de retorno (jr R31) copia o valor salvo de volta para o PC e a execução das instruções é retomada no endereço seguinte à instrução que chamou a rotina. A rotina pode então ser chamada de vários pontos do programa principal e o retorno é feito sempre para a instrução seguinte à chamada. As Chamadas ao Sistema Operacional O Sistema Operacional é um programa que é carregado quando o computador é ligado. A sua função é gerenciar e compartilhar os recursos do computador (memória, dispositivos de E/S, etc.) entre os diversos programas em execução (processos) no computador. O Sistema Operacional tem também uma função de proteção, ou seja, evitar que um programa de usuário interfira em programas ou arquivos de outro usuário, se não tiver autorização para isso. Para que o Sistema Operacional possa realizar estas funções é preciso que existam funções adequadas no processador. 23 As Chamadas ao Sistema Operacional Entre elas, destacamos a existência de dois modos de execução dos programas: um normal e outro privilegiado. Os programas de usuário executam em modo normal, isto é, algumas instruções não podem ser executadas, o acesso aos periféricos é restrito e o programa pode ver apenas a área de memória que foi reservada pelo Sistema Operacional. Os programas do núcleo do S.O. executam em modo privilegiado, podem fazer acesso a todos os dispositivos de E/S e a toda memória. A interface com o Sistema Operacional é feita através de uma instrução especial (chamada trap), que muda o estado da máquina para privilegiado, mas desvia imediatamente para endereços pré-fixados de rotinas do núcleo do S.O. As Chamadas ao Sistema Operacional Um ou mais parâmetros podem ser passados através dos registradores, de modo a orientar o Sistema Operacional sobre que tarefas são desejadas. Ao receber esse pedido, o Sistema Operacional verifica se o usuário tem privilégios adequados para o serviço que solicitou. Por exemplo: Se o programa de usuário solicita a abertura de um arquivo para escrita, os atributos do arquivo são verificados para saber se aquele usuário é o dono do arquivo e, caso não seja, se o seu dono permite que outros usuários possam escrevê-lo. Se as permissões estiverem de acordo, o serviço é então realizado. Senão é retornada uma mensagem de erro. 24 As Chamadas ao Sistema Operacional Depois de realizar as tarefas solicitadas, o Sistema Operacional retorna o controle para o programa de usuário, através de uma instrução de retorno especial, rett, que muda o modo do processador para o estado normal ao ser executada. A próxima instrução a ser executada nesse retorno está no endereço seguinte à instrução de trap que fez a chamada ao S.O. A função da Pilha O mecanismo descrito anteriormente para a chamada de procedimentos não permite que uma rotina seja chamada a partir de uma outra rotina. O valor do endereço de retorno seria perdido quando da execução de uma nova instrução de chamada de rotina. Para resolver este problema utiliza-se salvar o valor do endereço de retorno (R31) na memória, antes de chamarmos uma nova rotina, em uma estrutura de dados chamada pilha. Outros valores que são guardados na pilha são os parâmetros adicionais para a rotina, que não cabem nos registradores. 25 A função da Pilha A principal característica da pilha é que os elementos colocados por último são os primeiros a serem retirados. É uma estrutura que cresce do final da memória para o começo. Existe um registrador reservado para esta função do DLX chamado de apontador de pilha (sp), que indica o endereço do último elemento no “topo” da pilha. Antes do uso de qualquer registrador por uma rotina, os registradores antigos, que contenham valores que já possam estar em uso pelo programa, são também salvos na pilha. A função da Pilha Assim, os parâmetros de entrada são colocados na pilha, para serem utilizados pelas instruções na rotina. Antes de os parâmetros serem colocados na pilha, o apontador de pilha (sp) é decrementado (porque a pilha cresce no sentido inverso aos endereços da memória) com um valor igual ao tamanho total dos parâmetros que serão colocados na pilha. Algumas arquiteturas possuem instruções especiais que movem os valores para a pilha e, automaticamente, decrementam o valor do ponteiro de pilha (sp). A arquitetura DLX não possui instruções desse tipo e utiliza o registrador R29 como ponteiro de pilha. 26 Diretivas para o montador As diretivas são comandos que auxiliam o montador organizar os dados e instruções na memória de forma a obter-se uma execução adequada do programa em linguagem de montagem: .align n Allinha o dado seguinte em uma fronteira de 2n . .ascii str Armazena a cadeia str na memória. .asciiz str Armazena a cadeia str na memória e termina com “null” . .data <ender> Indica que os itens são dados e devem ser armazenados no endereço especificado por ender. Diretivas para o montador .text <ender> Indica que itens a seguir são instruções que devemser armazenadas no endereço especificado por ender. .globl simb Declara que o rótulo simb é global, podendo ser referenciado em em outros arquivos. .word w1, ..., wn Armazena as n quantidades de 32 bits em posições sucessivas de memória. 27 Instrução Exemplo Significado Add add R1, R2, R3 R1 = R2 + R3 Add Immediate addi R1, R2, 100 R1 = R2 + 100 Subtract sub R1, R2, R3 R1 = R2 - R3 Sub Immediate subi R1, R2, 100 R1 = R2 - 100 Branch Equal Zero beqz R1, endereço Branch on Not Equal Zero Set on Condition bnez R1, endereço Set on Condition Immediate Load word SCCi R1, R2, 100 Store word sw 100(R2), R1 Se (R1 == 0) faça PC = ender. senão PC=PC+4 Se (R1 != 0) faça PC = ender. senão PC=PC+4 Se (r2 CC r3) faça R1 =1 senão R1=0 Se (r2 CC 100) faça R1 =1 senão R1=0 R1 = Memória [R2+ 100] ( lê 4 bytes) Memória [r2+ 100] = r1 (escreve 4 bytes) SCC R1, R2, R3 lw Instrução R1, 100(R2) Exemplo R1, 100(R2) Significado Load word lw Store word sw 100(R2), R1 Memória [r2+ 100] = r1 (escreve 4 bytes) Load byte lb R1 = Memória [R2+ 200] (lê 1 byte) Store byte sb 200(R2),R1 Memória [R2+ 200] = r1 (escreve 1 byte) Shift Left Logical Immediate slli R1 = R2 << 2 (R1 = r2 * 4) Shift Right Logical Immed. srli R1, R2, 4 R1 = R2 >> 4 (R1 = R2 / 16) Jump j endereço Desvia para o endereço (PC = endereço) Jump Register jr R31 Desvia para R31 (PC = R31) Jump and Link jal endereço R31 = PC + 4; PC = endereço (chamada de procedimento) R1, 200(R2) R1, R2, 2 R1 = Memória [R2+ 100] ( lê 4 bytes) 28