ICC Linguagem de Máquina e Programas Já sabemos que um programa é uma seqüência de instruções que dirigem a CPU na execução de algum cálculo/tarefa. Os dados e as instruções de um programa, para serem manipulados ou executados pelo processador, precisam chegar a ele sob a forma de linguagem de máquina – impulsos elétricos codificados num sistema binário – pois as operações internas de um processador manipulam somente dois sinais elétricos (“tem corrente” , “não tem corrente”). Para facilitar a compreensão, representamos a presença de corrente (voltagem) pelo dígito 1 e a ausência de corrente (voltagem) pelo dígito 0. Os dígitos 0 e 1 são conhecidos por bits. Para ilustrar e facilitar o entendimento da operação de uma CPU, vamos apresentar um programa simples em linguagem de máquina e a ação de uma CPU ao executar as instruções contidas em tal programa. Vamos considerar uma CPU simples, com uma linguagem de máquina primitiva. De forma geral, qualquer linguagem nada mais é do que um conjunto de regras que especificam como as coisas devem ser expressas para serem compreendidas. No caso da linguagem da nossa máquina em particular, consideraremos um tamanho de 8 bits para cada instrução, dividindo-a em duas partes: um código de operação de 3 bits que especifica a operação a ser executada, mais um operando de 5 bits que indica o endereço do dado sobre o qual a operação será realizada. Também admitiremos a existência de um acumulador na unidade de aritmética e lógica no qual as operações aritméticas são executadas. A tabela a seguir define o conjunto de instruções desta nossa CPU simples (isto significa que a CPU tem circuitos eletrônicos que executam diretamente cada uma destas instruções em particular). Código de Operação 001 010 011 100 101 110 111 Significado Carregue: copie no acumulador o conteúdo da palavra (célula) endereçada Armazene: copie na palavra endereçada o valor do acumulador Some: substitua o conteúdo atual do acumulador pela soma de seu conteúdo com o conteúdo da palavra (célula) endereçada Subtraia: substitua o conteúdo atual do acumulador pelo resultado obtido da subtração entre este conteúdo e o conteúdo da palavra (célula) endereçada Desvie: salte para a instrução indicada pela palavra (célula) endereçada Desvie se diferente de zero: salte para a instrução na palavra (célula) endereçada se o valor contido no acumulador for diferente de zero Pare: encerre a execução Figura 1 – Exemplo de um conjunto de instruções -1- Vamos supor que nosso pequeno programa tenha sido carregado nas 10 primeiras palavras (células) da memória principal. Além das dez palavras indicadas, outras também receberam valores. A figura abaixo mostra o conteúdo completo da memória no início da execução do programa. Palavra Endereço em binário e o correspondente em decimal 00000 (0) 00001 (1) 00010 (2) 00011 (3) 00101010 01001100 00101110 01101011 00100 (4) 00101 (5) 00110 (6) 00111 (7) 01001110 00101100 10001101 01001100 01000 (8) 01001 (9) 01010 (10) 01011 (11) 11000010 11100000 00000011 00000100 01100 (12) 01101 (13) 01110 (14) 01111 (15) 00000000 00000001 00000000 00000000 Figura 2 – Conteúdo de memória do programa e seus dados Para facilitar a compreensão, o código de operação e o operando de cada instrução do programa carregado na memória são mostrados na tabela abaixo, em separado, apesar de estarem justapostos na memória, formando uma cadeia de 8 bits. Endereço da Instrução (binário) 00000 00001 00010 00011 00100 00101 00110 00111 01000 01001 Código de Operação 001 010 001 011 010 001 100 010 110 111 Operando 01010 01100 01110 01011 01110 01100 01101 01100 00010 00000 Figura 3 – Endereço de memória, código e operando de cada instrução do programa Vamos admitir que a execução comece no endereço 00000, com a instrução 00101010, e que as instruções sejam executadas na ordem em que aparecem, exceto menção em contrário. Com base na tabela da Figura 1, verificamos que a primeira instrução é CARREGUE (código de operação 001) a qual está ordenando à CPU que copie no seu acumulador o conteúdo do endereço 01010 (endereço 10 em decimal). Assim, de acordo com a instrução, o conteúdo 00000011 (3 em decimal) é colocado no acumulador. O fato desta operação ser apenas “copiar” significa que o conteúdo do endereço 01010 permanece inalterado. Entretanto, o conteúdo do acumulador foi substituído e se perdeu. A próxima instrução a ser executada, dentro da seqüência, está contida no endereço 00001. Esta segunda instrução é ARMAZENE (código de operação 010) a qual está ordenando à CPU que copie na palavra 01100 (12 em -2- decimal) o valor 00000011 (valor que está no acumulador), substituindo o anterior que ali se encontrava. A terceira instrução é novamente CARREGUE (código 001). O conteúdo atual da palavra com endereço 01110 (14 em decimal) é copiado no acumulador. O valor copiado é 00000000. A quarta instrução é SOME (código 011). O conteúdo (00000100, 4 em decimal) do endereço 01011 é somado ao conteúdo atual (00000000) do acumulador. A soma é realizada em binário no acumulador, resultando 00000100 (4 em decimal). Este valor substitui o conteúdo anterior do acumulador. A quinta instrução é ARMAZENE. Esta instrução faz com que o resultado da adição realizada anteriormente seja copiado do acumulador para o endereço 01110, substituindo o conteúdo anterior deste endereço. A sexta instrução é, novamente, CARREGUE. Agora o conteúdo da palavra de endereço 01100 (12 em decimal) é copiado no acumulador. Este valor é aquele armazenado pela Segunda instrução, ou seja, 00000011 (3 em decimal). A sétima instrução é SUBTRAIA (código 100). O conteúdo (00000001) do endereço 01101 (endereço 13) é subtraído do conteúdo do acumulador (00000011, 3 em decimal). A subtração é realizada em binário no acumulador, resultando o valor 00000010 (2 em decimal). A oitava instrução (010) é, novamente, ARMAZENE. O resultado obtido (00000010, valor 2 em decimal que está no acumulador) é armazenado na palavra com endereço 01100 (endereço 12). Neste ponto, vamos fazer uma pausa na computação para analisar o que aconteceu até aqui. O estado atual da memória e do acumulador está ilustrado na Figura 4. Comparando-se com o estado inicial da memória (Figura 2), nota-se que ocorreram alterações de conteúdos. As palavras afetadas foram as de endereços 01100 e 01110 (em cinza). De fato, devemos observar estas duas palavras à medida que a computação prossegue. 00000 (0) 00001 (1) 00010 (2) 00011 (3) 00101010 01001100 00101110 01101011 00100 (4) 00101 (5) 00110 (6) 00111 (7) 01001110 00101100 10001101 01001100 01000 (8) 01001 (9) 01010 (10) 01011 (11) 11000010 11100000 00000011 00000100 01100 (12) 01101 (13) 01110 (14) 01111 (15) 00000010 00000001 00000100 00000000 Acumulador : 00000010 Figura 4 – Conteúdo da memória e acumulador após execução de oito instruções. -3- Vamos passar agora para a nona instrução. A Figura 1 indica que esta instrução é DESVIE SE DIFERENTE DE ZERO (código 110). Esta instrução é de um tipo diferente das que vinham sendo executadas até agora. Ela indica duas coisas. Primeiro, examine o conteúdo atual do acumulador. Se o valor contido no acumulador é diferente de zero, o que é verdade neste momento (00000010), então desvie imediatamente para a instrução cujo endereço é dado no operando (00010, vai executar a instrução nesta célula. Célula 2 em decimal). Esta será a próxima instrução a ser executada. Se o conteúdo do acumulador é igual a zero, então, ao invés de desviar para a instrução do endereço 00010, deve-se prosseguir executando a próxima instrução dentro da seqüência, ou seja, a instrução localizada em 01001. Esta instrução é chamada de Desvio Condicional. No caso acima, houve um desvio para a terceira instrução (endereço 00010 ou 2). Como é de se esperar, as instruções de três a oito serão executadas novamente. Este trabalho ficará como exercício (lembre-se que você deverá iniciar agora da situação mostrada na Figura 4. Ao passar pela segunda vez por este grupo de instruções, você deverá obter a situação ilustrada na Figura 5, abaixo. 00000 (0) 00001 (1) 00010 (2) 00011 (3) 00101010 01001100 00101110 01101011 00100 (4) 00101 (5) 00110 (6) 00111 (7) 01001110 00101100 10001101 01001100 01000 (8) 01001 (9) 01010 (10) 01011 (11) 11000010 11100000 00000011 00000100 01100 (12) 01101 (13) 01110 (14) 01111 (15) 00000001 00000001 00001000 00000000 Acumulador: 00000001 Figura 5 – Conteúdo da memória e acumulador após a segunda execução do laço. Neste ponto, a instrução de desvio condicional é encontrada pela segunda vez. Novamente, o conteúdo do acumulador não é zero e o desvio é executado uma outra vez. Este trabalho ficará como exercício. Após a terceira execução da instrução oito, os conteúdos da memória e do acumulador são mostrados na Figura 6. Em particular, o conteúdo da palavra 01100 é 00000000, o da palavra 01110 é 00001100 (12 em decimal) e o conteúdo do acumulador é 00000000. 00000 (0) 00001 (1) 00010 (2) 00011 (3) 00101010 01001100 00101110 01101011 00100 (4) 00101 (5) 00110 (6) 00111 (7) 01001110 00101100 10001101 01001100 01000 (8) 01001 (9) 01010 (10) 01011 (11) 11000010 11100000 00000011 00000100 01100 (12) 01101 (13) 01110 (14) 01111 (15) 00000000 00000001 00001100 00000000 Acumulador: 00000000 -4- Figura 6 – Conteúdo da memória e acumulador no final da execução do programa. Neste ponto, a instrução de desvio condicional é encontrada pela terceira vez mas não é executada, pois o conteúdo do acumulador é 0. Conseqüentemente, passamos para a instrução do endereço 01001, que é a seguinte na seqüência. Esta é uma instrução PARE (código 111). Assim, a execução do programa é terminada. A partir da Figura 6, podemos verificar os resultados finais obtidos. Particularmente, o valor da palavra de endereço 01110 é 12 em decimal. Agora, você poderá refletir sobre o que aconteceu, dando atenção especial aos valores iniciais dados pela Figura 2, e tentar descobrir qual foi a finalidade deste programa. Conclusão A discussão anterior descreveu como um computador típico opera. A linguagem de máquina utilizada no exemplo foi elaborada apenas a título de ilustração, entretanto, as linguagens reais de máquina têm propriedades semelhantes. O papel de um programa foi identificado como o de fornecer instruções particulares sob as quais o controle da CPU opera para executar algum cálculo requerido. A tarefa de um programador é produzir a seqüência apropriada de instruções para resolver algum problema em pauta. -5-