Instituto Politécnico Curso: Tec. Redes de Computadores Disciplina: Organização de Computadores Prof.: Fábio Lucena Veloso Curso: Tec. Análise de Sistemas Disciplina: Organização de Computadores Prof.: Roberto Barros Profs. Fábio Lucena Veloso e Roberto Barros 1 Conjunto de Instruções: Características de instruções de máquina: A operação de uma CPU é determinada pelas instruções que ela executa, conhecidas como instruções de máquina ou instruções do computador. A coleção de diferentes instruções que a CPU é capaz de executar é conhecida como conjunto de instruções da CPU. Elementos de instruções de máquina Cada instrução deve conter toda a informação necessária para que a CPU possa executála. A Figura 22, mostra os passos envolvidos na execução de instruções, definindo os elementos de instruções de máquina: Código de operação: especifica a operação a ser efetuada (por exemplo, ADD, E/S). A operação é especificada por um código binário, conhecido como código de operação. Referência a operando fonte: a operação pode envolver um ou mais operandos fonte, ou seja, operandos que constituem dados de entrada para a operação. Referência a operando de destino: a operação pode produzir um resultado. Endereço da próxima instrução: indica onde a CPU deve buscar a próxima instrução, depois que a execução da instrução corrente for completada. A próxima instrução a ser buscada pode estar localizada na memória principal ou, no caso de um sistema com memória virtual, tanto na memória principal quanto na memória secundaria (disco). Na maioria dos casos, a próxima instrução é a que segue imediatamente a instrução corrente. Nesses casos, a instrução não inclui uma referência explícita para a próxima instrução. Quando isso é necessário, a instrução deve fornecer um endereço de memória principal ou de memória virtual. Figura 22 - Diagrama de estados do ciclo de instruções. Profs. Fábio Lucena Veloso e Roberto Barros 2 Os operandos fonte e de destino podem estar localizados em uma das seguintes áreas: Memória principal ou virtual: assim como na referência para a próxima instrução, deve ser fornecido um endereço, que pode ser na memória principal ou na memória virtual. Registrador da CPU: com raras exceções, a CPU contém um ou mais registradores, que podem ser referenciados pelas instruções de máquina. Se existir apenas um único registrador, a referência a ele poderá ser implícita. Se existirem vários registradores, então, cada registrador será designado por um número distinto, e a instrução deverá conter o número do registrador desejado. Dispositivo de E/S: a instrução deve especificar um módulo de E/S e um dispositivo para a operação. Se for usada a E/S mapeada na memória, essa informação consistirá apenas em um endereço na memória principal ou na memória virtual. Representação de instruções Internamente, cada instrução de um computador é representada como uma seqüência de bits. Uma instrução é dividida em campos, correspondentes aos elementos da instrução. Um exemplo simples de formato de instrução é mostrado na Figura 23. Na maioria dos conjuntos de instruções, é usado mais de um formato de instrução. Durante a execução, uma instrução é lida em um registrador de instruções (IR) da CPU. A CPU deve ser capaz de extrair os dados dos vários campos da instrução e efetuar a operação requerida. Fig. 23 É difícil para o programador lidar com representações binárias de instruções de máquina. Por isso, tornou-se prática comum usar uma representação simbólica para instruções de máquina. Os códigos de operação são representados por abreviações, chamadas mnemônicos, que indicam a operação a ser efetuada. Alguns exemplos comuns são: ADD Adição SUB Subtração MPY Multiplicação DIV Divisão LOAD Carregar dados da memória STOR Armazenar dados na memória Os operandos são também representados de maneira simbólica. Por exemplo, a instrução: ADD R,Y pode significar adicionar o valor contido na posição Y com o conteúdo do registrador R. Nesse exemplo, Y é um endereço de uma posição de memória e R indica um registrador Profs. Fábio Lucena Veloso e Roberto Barros 3 particular. Note que a operação é feita sobre o conteúdo da posição de memória, e não sobre seu endereço. Portanto, é possível escrever um programa em linguagem de máquina de maneira simbólica. Cada código de operação simbólico tem uma representação binária correspondente, e o programador especifica o endereço de cada operando simbólico. Por exemplo, o programador pode começar com a seguinte lista de definições: X = 513 Y = 514 e assim por diante. Essa entrada simbólica pode ser convertida, por meio de um programa bastante simples, em códigos de operação e referências a operandos na forma binária, resultando em instruções de máquina binárias. Hoje, é muito raro programar em linguagem de máquina. A maioria dos programas é escrita em linguagem de alto nível ou, em alguns casos, em linguagem de montagem. No entanto, a linguagem de máquina simbólica permanece como uma ferramenta útil para descrever instruções de máquina, sendo usada a seguir para esse propósito. Tipos de instrução Considere uma instrução em uma linguagem de alto nível, tal como BASIC ou FORTRAN. Por exemplo: X = X + Y Esse comando instrui o computador a adicionar o valor armazenado em Y ao valor armazenado em X e colocar o resultado em X. Como isso pode ser feito usando instruções de máquina? Suponha que as variáveis X e Y correspondam às posições de memória de endereços 513 e 514. Se considerarmos um conjunto simples de instruções de máquina, esse comando pode ser implementado com três instruções: 1) Carregar um registrador com o conteúdo da posição de memória 513. 2) Adicionar o conteúdo da posição de memória 514 ao registrador. 3) Armazenar o conteúdo do registrador na posição de memória 513. Como se pode observar, uma única instrução em linguagem de alto nível pode requerer várias instruções de máquina. Isso é típico do relacionamento entre uma linguagem de alto nível e uma linguagem de máquina. Em uma linguagem de alto nível, as operações são expressas de uma maneira algébrica concisa, usando variáveis. Em uma linguagem de máquina, as operações são expressas de maneira mais básica, envolvendo a movimentação de dados de e para registradores. Tendo como base esse exemplo simples, consideramos os tipos de instruções que devem ser incluídos em um computador. Um computador deve ter um conjunto de instruções que permita ao usuário formular qualquer tarefa de processamento de dados. Outra Profs. Fábio Lucena Veloso e Roberto Barros 4 maneira de determinar esse conjunto de instruções é considerar os comandos disponíveis em uma linguagem de programação de alto nível. Qualquer programa em uma linguagem de alto nível deve ser traduzido para uma linguagem de máquina, para que possa ser executado. Portanto, o conjunto de instruções de máquina deve ser suficiente para expressar qualquer comando de uma linguagem de alto nível. Com isso em mente, podemos, então, catalogar os tipos de instruções de máquina como a seguir: a) Processamento de dados: instruções aritméticas e lógicas b) Armazenamento de dados: instruções de memória c) Movimentação de dados: instruções de E/S d) Controle: instruções de teste e de desvio Instruções aritméticas fornecem a capacidade computacional para processamento de dados numéricos. Instruções lógicas (booleanas) operam sobre bits de uma palavra, como bits e não como números; oferecem, portanto, a capacidade para processar qualquer outro tipo de dado que o usuário possa desejar empregar. Essas operações são efetuadas, primariamente, em dados armazenados em registradores da CPU. Por isso, devem existir instruções de memória para mover dados entre a memória e os registradores. Instruções de E/S são necessárias para transferir programas e dados para a memória e para transferir resultados da computação de volta para o usuário. Instruções de teste são usadas para testar o valor de uma palavra de dados ou o estado de uma computação. Instruções de desvio são utilizadas para desviar a execução do programa para uma nova instrução, possivelmente dependendo do resultado de um teste. Número de endereços Uma das maneiras tradicionais de descrever uma arquitetura é em termos do número de endereços contidos em cada instrução. Esse aspecto tornou-se menos significativo com a crescente complexidade de projeto de CPU. Entretanto, é útil considerar e analisar essa distinção entre instruções com diferentes números de endereços. Qual é o número máximo de endereços necessários em uma instrução? Evidentemente, instruções aritméticas e lógicas requerem maior número de operandos. Quase todas as operações aritméticas e lógicas são unárias (um operando) ou binárias (dois operandos). Portanto, precisamos, no máximo, de dois endereços para referenciar operandos. Como o resultado da operação deve ser armazenado, isso sugere que é necessário um terceiro endereço. Finalmente, depois de concluída a execução de uma instrução, a próxima instrução deve ser buscada, sendo necessário conhecer seu endereço. Essa linha de raciocínio sugere que poderia ser necessário ter instruções com quatro endereços: dois para operandos, um para o resultado e o endereço da próxima instrução. Na prática, instruções com quatro endereços são extremamente raras. A maioria das instruções tem um, dois ou três endereços de operando, sendo implícito o endereço da próxima instrução (contido no contador de programa). 5 Profs. Fábio Lucena Veloso e Roberto Barros A Figura 24 compara instruções típicas de um, dois e três endereços, que podem ser usadas para computar o comando Y = (A - B) / (C + D x E). Com instruções de três endereços, cada instrução especifica dois endereços de operandos e um endereço para o resultado. Como podemos querer não alterar o valor de qualquer posição de memória, uma área de memória temporária, T, é usada para armazenar resultados intermediários. Note que a implementação do comando requer quatro instruções e que a expressão original tem cinco operandos. Instrução SUB XA, B MP XD,E Y AD XX C Comentário Y<- A – B T<- D x E Instrução MO XA SUB XB Comentário Y<- A Y<r- Y – B Instrução LOAD MPY T<- T + C ADD D DIV XXT Y<- Y -5- T STOR (a) Instruções com três endereços LOAD SUB D E C Y AB Comentário AC<-D AC <—AC x E AC<-AC+C Y <-AC AC ^~A AC*AC- B DIV Y AC<-AC-Y STOR Y Y *-AC (c) Instruções com um endereço MO XD T<- D VE MP XE T<- T x E Y AD XC T<- T 4- C D DIV XT Y<- Y + T (b) Instruções com dois endereços Figura 24 - Programas para executar o comando Y = (A - B) / (C + DxE). Formatos de instrução com três endereços não são muito comuns, porque resultam em instruções de tamanho relativamente grande, devido ao espaço necessário para manter os três endereços. No caso de instruções com dois endereços e das operações binárias, um dos endereços referencia tanto um operando quanto o resultado. Por exemplo, a instrução SUB Y, B calcula o valor Y - B e armazena o resultado em Y. O uso do formato de instrução com dois endereços reduz o tamanho das instruções, mas apresenta algumas desvantagens. Para evitar que se altere o valor de um operando, é usada uma instrução MOVE, para mover um dos operandos para a posição de resultado ou para uma posição temporária, antes de a operação ser efetuada. O número de instruções requeridas para implementar o exemplo anterior aumentaria, nesse caso, para seis instruções. Instruções de apenas um endereço são ainda mais simples. Nesse caso, um segundo endereço deve ser implícito. Esse tipo de instrução era comum nas primeiras máquinas, onde o endereço subentendido é um registrador da CPU, conhecido como acumulador (ACC). O acumulador contém um dos operandos e é usado para armazenar o resultado. 6 Profs. Fábio Lucena Veloso e Roberto Barros É possível, ainda, usar um formato de instrução com zero endereço, para alguns tipos de instrução. Esse formato se aplica a uma organização de memória especial, denominada pilha. Número de endereços Representação simbólica OP A,B,C OP A,B OP A OP 3 2 1 0 Acc = acumulador T = topo da pilha A, B, C = registradores ou posições de memória Interpretação A A Acc T B OP C A OP B Acc OP A (T – 1) OP T A Tabela acima apresenta um resumo da interpretação de instruções com zero, um, dois ou três endereços. Em cada caso, supomos que o endereço da próxima instrução a ser executada seja implícito e que a operação requeira dois operandos como entrada e um operando como resultado. O número de endereços por instrução constitui uma decisão de projeto importante. Poucos endereços por instrução resultam em instruções de menor extensão e mais primitivas, que requerem uma CPU menos complexa. Por outro lado, o número de instruções por programa é maior, o que, em geral, resulta em maior tempo de execução e em programas mais complexos. Além disso, existe outro aspecto importante em relação ao quais instruções de um ou múltiplos endereços se contrapõem. Com instruções de um endereço, o programador geralmente tem disponível apenas um registrador de propósito geral, o acumulador. Com instruções de múltiplos endereços, é comum haver múltiplos registradores de propósito geral. Isso possibilita que algumas operações sejam efetuadas apenas sobre registradores. Como referências a registradores são mais rápidas do que referências à memória, a execução dessas instruções é mais rápida. Em razão da flexibilidade e da capacidade para usar múltiplos registradores, a maioria das máquinas modernas emprega instruções de dois ou de três endereços. As questões de projeto envolvidas na escolha do número de endereços por instrução são complicadas ainda por outros fatores. Um deles consiste em decidir se um endereço se refere a uma posição de memória ou a um registrador. Como o número de registradores é menor que o número de posições de memória, um número menor de bits é requerido para endereçar um registrador. Além disso, uma máquina pode oferecer uma variedade de modos de endereçamento, requerendo um ou mais bits para especificar o modo de endereçamento. Como resultado, a maioria dos projetos de CPUs envolve uma variedade de formatos de instrução. Projeto do conjunto de instruções. Um dos aspectos mais interessantes e mais analisados do projeto de computadores é o projeto do conjunto de instruções. O projeto do conjunto de instruções é muito complexo, pois afeta diversos aspectos do sistema. Ele define muitas das funções Profs. Fábio Lucena Veloso e Roberto Barros 7 desempenhadas pela CPU e, portanto, tem efeito significativo sobre a implementação da CPU. Como o conjunto de instruções constitui o meio pelo qual o programador pode controlar a CPU, ao projetar um conjunto de instruções é preciso considerar as necessidades do programador. Você poderá se surpreender ao saber que algumas das questões mais fundamentais relativas ao projeto de conjuntos de instruções permanecem ainda em discussão. De fato, nos últimos anos, tem crescido a discordância a respeito dessas questões. Algumas das questões mais importantes são as seguintes: a) Repertório de operações: quantas e quais são as operações que devem ser fornecidas e quão complexas elas podem ser. b) Tipos de dados: quais os tipos de dados sobre os quais as operações são efetuadas. c) Formatos de instrução: qual o tamanho das instruções (em bits), o número de endereços por instrução, o tamanho dos vários campos etc. d) Registradores: qual o número de registradores da CPU que podem ser usados pelas instruções e qual o propósito de cada um. e) Endereçamento: de que modo (ou modos) o endereço de um operando pode ser especificado. Essas questões são altamente inter-relacionadas e devem ser consideradas em conjunto ao se projetar um conjunto de instruções. Tipos de operandos Instruções de máquina operam sobre dados. As classes de dados mais importantes são: 1) Endereços 2) Números 3) Caracteres 4) Dados lógicos Ao discutir os modos de endereçamento, veremos que os endereços são, de fato, uma forma de dado. Em muitos casos, é necessário efetuar cálculos sobre o valor do campo de endereço de um operando de uma instrução para determinar o endereço de memória principal ou virtual correspondente. Nesse contexto, os endereços podem ser considerados números inteiros sem sinal. Outros tipos de dados comuns são números, caracteres e dados lógicos; cada um será discutido detalhadamente adiante nesta apostila Profs. Fábio Lucena Veloso e Roberto Barros 8 Representação das Instruções Podemos representar o formato padrão da instrução com um operador conforme o diagrama abaixo. Este formato não é o único utilizado nos sistemas de computação. Na realidade o conjunto de instruções é sempre constituído por uma mistura de formatos diferentes. A tendência é fazer com que os computadores operem quase sempre com uma pequena quantidade de instruções. Pode-se efetuar a análise segundo dois aspectos: 1) Quantidade de operandos. 2) Modo de interpretação (endereçamento) do valor armazenado no campo operado. Quantidade de Operandos: Instruções de máquinas são constituídas de um conjunto de beta, o qual contém um subconjunto chamado código de operação, que identifica a operação a ser realizada pelo hardware. Este código é decodificado na UC na fase de instrução, gerando os pulsos de controle para acionar as portas lógicas necessárias a execução da operação. Possui ainda um ou mais grupos de bits denominados campo(s) do(s) operando(s) que tem por função identificar e localizar o dado a ser processado. • • • • 4 operandos (não mais usada) = Possui a indicação explícita da localização de todos os operandos como também traz armazenado o endereço da próxima instrução; 3 operandos = Os campos 1 e 2 representam o endereço de cada dado utilizado em uma operação, já o campo 3 contém o endereço para armazenamento do resultado dessa operação; 2 operandos = O campo 1 recebe o resultado da operação; 1 operando = O registrador ACC (acumulador) é empregado como operando implícito guardando o valor de um operando e posteriormente o valor do resultado da operação. 9 Profs. Fábio Lucena Veloso e Roberto Barros Detalhes: 4 Operandos: Cód. Op. Ex.: Operando 1 Operando 2 Soma -> C = A + B ADD A, B, C, P Operando 3 End. próx. instr. (C) <- (A) + (B), P Onde P é o endereço da próxima instrução. Vantagens: Ser uma instrução completa, pois possui todos os operandos necessários a realização de uma operação aritmética, dispensando até a instrução de desvio incondicional, pois essa já se encontra no campo P. Desvantagens: Ocupa demasiado espaço de memória, principalmente porque um grande n° de instruções não necessita de três operandos. 3 Operandos: Cód. Op. Ex.: Operando 1 Operando 2 C=A+B ADD A, B, C Operando 3 (C) <- (A) + (B) Desvantagem: Ainda é muito grande, consumindo demasiado espaço de memória. 2 Operandos: Cód. Op. Ex.: Operando 1 Operando 2 ADD A, B (A) <- (A) + (B) MOV A, B (A) <- (B) Obs: Observe que o valor do 1° operando se perde após o armazenamento naquele endereço do resultado da operação. 1 Operando: Cód. Op. Ex.: Operando ADD Op Onde “Op” é o operando. ACC <- ACC + (Op) 10 Profs. Fábio Lucena Veloso e Roberto Barros Obs: Observem que o valor do 1° operando se perde após a realização da operação, pois o resultado da operação será armazenado nesse local. Obs: Para esse tipo, foram criadas duas novas instruções com o propósito de permitir a transferência de dados entre o ACC e a MP (Memória Principal) LDA Op STA Op => => ACC (Op) (Op) ACC Modos de Endereçamento Pelo formato básico da instrução e o ciclo de execução de cada instrução podemos concluir: a) O endereçamento de uma instrução é sempre realizado através do valor do armazenamento no contador de instrução (CI). Todo o ciclo de instrução é iniciado pela transferência da instrução para o RI. b) Toda a instrução consiste em uma ordem codificada (código da operação) para a CPU executar uma operação qualquer sobre os dados. c) A localização dos dados pode estar explicitamente indicada na própria instrução (campos operando) ou implicitamente quando armazenado no acumulador (ACC). Principais modos de endereçamento Dentre os diversos modos de endereçamento atualmente empregados podemos destacar os principais: 1) Imediato: indica o valor do dado no campo operando da instituição em vez de buscálo na memória, tem como vantagem o curto tempo de execução da instrução por não gastar ciclo de memória para sua execução. O dado transferido da memória junto com a instrução por esta estar contida no campo operando da instrução. Ex.: MOV Reg. Op Cód. Op. 4 Ex: Cód. Op. 4 Reg 4 JMP Op Operando 8 bits Reg <- valor de Op Operando 8 bits Armazena o valor de Op no Acc => Acc <- Op 11 Profs. Fábio Lucena Veloso e Roberto Barros 2) Direto: o valor binário contido no campo operando da instrução indica o endereço de memória onde se localiza o dado. O endereço pode ser uma célula onde o dado está inteiramente contido ou pode indicar o endereço da célula inicial. Requer apenas uma referência a MP para buscar o dado sendo, porém mais sendo mais lento que o imediato. Ex 1.: LDA Op Cód. Op. 4 Ex 2.: Acc <- (Op) ex.1 -> LDA 3B Operando 8 bits ADD Op1, Op2 Cód. Op. 4 (Op1) <- (Op1) + (Op2) Operando 1 4 ex. 2 -> ADD 5C, 3B Operando 2 8 bits ex. 1: O ACC receberá o valor 5A 3B 5C 5A 103 15D ex. 2: Somar o conteúdo da posição de memória Op1 (5C) de dado (103) com o conteúdo da posição de memória Op2 (3B) de dado (5A) e armazenar o resultado na posição de memória Op1 (5C) com valor = 15D (dado) 12 Profs. Fábio Lucena Veloso e Roberto Barros 3) Indireto: o valor binário do campo operando representa o endereço de uma célula, mas o conteúdo da referida célula não é o valor de um dado e sim um outro endereço de memória, cujo conteúdo é o valor do dado. Há um duplo endereçamento para o acesso do dado, em conseqüência mais ciclos de memória. O endereço intermediário é conhecido como ponteira, pois indica a localização do dado (aponta para o dado) Cód. Op. 4 Operando 8 bits ou Cód. Op. Reg Campo que especifica o endereçamento indireto 1000 1510 Operando Ex. Reg 1 Ex. End. 1000 Passo 1: O operador para o operando é buscado da posição 1000. Passo 2: O operando é buscado da posição 1510. 1510 4 R1 4 Passo 3: O operando é carregado em R1. Obs: Existem diferentes modos de se utilizar esse endereçamento: - Cada código de operação estabelece não só o tipo de instrução como também o modo de endereçamento. - A instrução possui um campo específico para indicar o modo de endereçamento 4) Por registrador: semelhante ao direto e indireto exceto que a célula de memória referenciada na instrução é substituída por um registrador da CPU. O endereço mencionado na instrução passa a ser o de um dos registradores e não mais uma célula da MP. Tem como vantagem um menor numero de bits para endereçar os registradores. O dado passa a ser armazenado em um meio com acesso mais rápido que o acesso à memória. 13 Profs. Fábio Lucena Veloso e Roberto Barros 5) Indexado: o endereço do dado é a soma do valor do campo operando (fixo para um dado vetor) e de um valor armazenado neste registrador varia para o acesso a cada elemento. O índice aponta para o elemento desejado. 6) Base mais deslocamento: consiste na utilização de dois campos na instrução, um com o endereço de um registrador (chave base) e outro com um valor denominado deslocamento, porque contem um valor relativo ‘a primeira construção. 7) Endereçamento de pilha: aqui temos instruções sem endereços, apenas códigos de operação. Isto é possível através de uma estrutura denominada pilha. Uma pilha consiste em um conjunto de posições de memória, que são manipuladas de maneira que cada leitura efetuada sobre a pilha recupere o último dado nela armazenado (topo da pilha), retirando-o da pilha. A área de memória reservada para a pilha inicia a partir de um endereço fixo na memória e, usualmente, pelo menos os dois elementos no topo da pilha são armazenados em registradores da CPU. Instruções de zero endereços referenciam os dois elementos no topo da pilha. Quadro comparativo entre os três primeiros modos de endereçamento. Modos de Endereçamento Imediato Definição Vantagens Desvantagens O campo operando contém o dado Rapidez na execução da instrução Direto O campo operando contém o endereço do dado Indireto O campo operando contém o endereço do dado Flexibilidade no acesso a variáveis de valor diferente em cada execução do programa Manuseio de vetores (quando o modo indexado não está disponível). Uso como “ponteiro” Limitação do tamanho do dado. Inadequado para o uso com dados de valor variável Perda de tempo, se o dado é uma constante Muitos acessos à memória principal para execução Profs. Fábio Lucena Veloso e Roberto Barros 14 Representação de dados Os computadores executam instruções que realizam operações sobre valores (numéricos, alfanuméricos ou lógicos). Esses valores são conhecidos como dados. Os valores de dados normalmente são introduzidos no computador através de digitação, e são imediatamente transformados em algum código padrão, que depende do computador que está recebendo o dado e alguns caracteres podem ser armazenados nesse código padrão (dados alfanuméricos), enquanto outros podem ser armazenados em formato mais adequado para a realização de operações sobre eles (dados numéricos ou lógicos). Tipo de dado De um modo geral, as seguintes formas de dados são utilizadas nos programas atuais (formas primitivas) • • • Tipo caractere Tipo lógico Tipo numérico Tipos primitivos de dados: Existem ainda formas mais complexas permitidas em certas linguagens modernas, mas durante a compilação estes dados são convertidos para as formas primitivas. 15 Profs. Fábio Lucena Veloso e Roberto Barros Tipo caractere Os computadores só trabalham com dígitos binários, valendo 0 ou 1. Para representar os símbolos (ou caracteres) que reconhecemos a partir de 0s e 1s, deve ser utilizada uma técnica chamada de codificação, onde cada símbolo de nossa linguagem é associado biunivocamente a um grupo de bits. A codificação é a forma de representar os caracteres armazenados no computador. O tamanho desse grupo de bits é definido a partir do número de diferentes símbolos que queremos representar, uma vez que já sabemos que n bits podem representar até 2n símbolos diferentes. Desde o advento da computação foram criados vários sistemas de codificação, dentre os quais podemos destacar: a) BCD - Binary Coded Decimal (Decimal codificado em binário) - grupos de 6 bits, representando 64 caracteres (obsoleto). b) EBCDIC - Extended Binary Coded Decimal Interchange Code (Código para intercâmbio BCD estendido) - Usado em mainframes IBM. Grupos de 8 bits, permitindo 256 caracteres diferentes, mas na prática muitos códigos não são associados a nenhum caractere legível. c) ASCII - American Standard Code for Information Interchange (Código padrão americano para troca de informações) - Criado com grupos de 7 bits, e mais um para paridade, foi estendido para 8 bits, devido à necessidade de aumentar o conjunto de 128 caracteres para 256, devido à introdução de símbolos gráficos, e letras acentuadas. d) Unicode - Grupos de 16 bits, criado para suportar todos os símbolos da humanidade, como caracteres japoneses, chineses, árabes, hebraicos, etc. Comporta até 64K símbolos diferentes (mais de 65.000). Para manter compatibilidade com a tabela ASCII atual, todos os símbolos ASCII permanecem como estão, sendo adicionados 8 bits zero após os atuais 8 bits. Tabelas ASCII e EBCDIC são encontradas em diversas publicações e programas de computador, e não são facilmente decoráveis em seu todo, mas é útil conhecer as representações de alguns símbolos nesses códigos. Os valores dos símbolos normalmente são mostrados nas tabelas em hexadecimal. Caractere Espaço Dígitos 0 a 9 Letras A - Z (a - z) Letras A - I (a - i) Letras J - R (j - r) Letras S - Z (s - z) ASCII 20 30 a 39 41 a 5A (61 a 7A) EBCDIC 40 F0 a F9 C1 a C9 (81 a 89) D1 a D9 (91 a 99) E2 a E9 (A2 a A9) 16 Profs. Fábio Lucena Veloso e Roberto Barros Notas: No código ASCII as letras minúsculas são obtidas adicionando-se 20 as maiúsculas, enquanto no EBCDIC as letras minúsculas são obtidas subtraindo-se 40 das maiúsculas. Em ASCII as letras são contíguas, permitindo que ao se programar, possa-se fazer letra2 = letra1 + 1, para obter a letra seguinte a uma outra, enquanto que isso não é permitido em EBCDIC. Exemplo: "J" = "I" + 1 em ASCII e "J" = "I" + 8 em EBCDIC. Com esse conjunto de caracteres pode-se representar qualquer informação, e até processá-las. Entretanto, existem outras formas de se armazenar valores matemáticos no computador, que se por um lado não são fáceis de serem interpretados pelas pessoas, são muito mais eficientes de serem processados pelos computadores. Chamamos a essas representações de representações internas. Elas também dependem da arquitetura do computador. Quando o usuário, utilizando uma linguagem de programação, introduz um programa no computador, os componentes de sua estrutura física convertem os caracteres introduzidos como um texto livre, para o código de bits usados pela máquina, em seguida traduz para o código-objeto e passa os elementos do programa para uma representação passível de ser interpretada e manipulada pelo hardware (CPU) Tipo lógico Esse tipo de dados permite armazenar apenas 2 informações distintas: • • Verdadeiro (True): bit um Falso (False): bit zero Como já vimos anteriormente, estas variáveis são utilizadas de diversas formas em um programa, podendo ser realizado um tipo específico de operação, empregando operações lógicas. Apesar de ser necessário apenas 1 bit para armazenar 1 valor entre 2 possíveis, as implementações nas linguagens de programação utilizam 1 ou mais bytes para armazenar um valor lógico (ou booleano). Assim, em Pascal pode-se declarar ByteBool (lógico com 1 byte) ou WordBool (lógico com 1 palavra Æ 2 ou 4 bytes, dependendo do tamanho da palavra). Em Visual Basic, a declaração Boolean cria variáveis lógicas com 2 bytes de tamanho. Em COBOL não existe este tipo de dado, apesar de existirem expressões lógicas, como A > B e os operadores lógicos NOT, AND e OR. Profs. Fábio Lucena Veloso e Roberto Barros 17 Os operadores lógicos mais comuns em linguagens de programação são: a) NOT. Operador unário. Inverte o valor lógico da variável ou expressão, devolvendo True se a variável for False, e False se a variável for True. Exemplos: NOT A; NOT (VALOR > 10) b) AND. Operador binário. A AND B devolve True se A e B forem True, e devolve False em caso contrário. c) OR. Operador Binário. A OR B devolve True se pelo menos 1 dos operandos for True, e devolve False se ambos forem False d) XOR (OU EXCLUSIVO). A XOR B devolve True se exatamente 1 dos operandos for TRUE, e False se ambos forem False ou True. Tipo numérico A ULA foi projetada para, rapidamente, executar operações com valores recebidos em forma binária. Um dos primeiros problemas ocorridos quando da representação de números nos computadores foi na indicação do sinal do número (+ ou -) Isso foi resolvido com o acréscimo de mais um bit na representação do número, esse bit adicional representa o sinal do número e é denominado sinal e magnitude. A convenção adotada de forma universal é: • • Valor positivo - bit de sinal igual a 0 Valor negativo - bit de sinal igual a 1 Outro problema é a forma de representação dos números fracionários, devido à dificuldade de se representar à vírgula internamente entre a posição dos dois bits. Existem dois modos de representação: • • Ponto fixo (vírgula fixa) – consiste em adotar uma posição fixa para a vírgula Ponto flutuante (vírgula flutuante) – utiliza a representação conhecida como notação científica Representação em ponto fixo Os valores fracionários também representam um problema a ser resolvido. A solução mais simples é guardar o número como se fosse inteiro, e lembrar que certo número de dígitos são fracionários para posterior apresentação ou operação. Posições mais adotadas: • Na extremidade esquerda – nesse caso o número seria totalmente fracionário; • Na extremidade direita – nesse caso o número seria inteiro Profs. Fábio Lucena Veloso e Roberto Barros 18 Em nenhum dos casos a vírgula estará representada fisicamente na memória, isto é feito via programa por ocasião da definição da variável, realizada pelo programador. O sistema memoriza essa posição, mas não a representa fisicamente. Esse modo é chamado de representação em ponto fixo. Na linguagem C, os tipos definidos são: CHAR (1 byte), INT (2 bytes) e LONG INT (4 bytes). Em Visual Basic, temos os tipos BYTE, INTEGER (2 bytes) e LONG (4 bytes). Nesta linguagem existe ainda um tipo chamado DECIMAL com 14 bytes, contendo um valor inteiro e um fator de escala, indicando o número de casas decimais e podendo valer, se não tiver parte fracionária, +/-79.228.162.514.264.337.593.543.950.335, e com partes decimais, esse mesmo valor, mas com a vírgula decimal em qualquer posição a partir do dígito 7 inicial. O menor número em valor absoluto é 10-28. Temos também em VB o tipo CURRENCY (monetário) com 8 bytes de comprimento, com 4 casas decimais, permitindo uma precisão de -922.337.203.685.477,5808 até 922.337.203.685.477,5807. Na representação de números em ponto fixo, os valores positivos são representados pelo bit 0 de sinal. Sendo posicionado como algarismo mais representativo (à esquerda) este grupo de bits representará o valor absoluto do número (magnitude) Quanto aos números negativos o sinal é representado pelo bit 1 e a magnitude pode ser representada por um dos três modos: a)Sinal e magnitude b) Complemento c) Complemento a base menos 1 Sinal e Magnitude A representação de números com n algarismos binários (n bits) em sinal e magnitude é obtida atribuindo-se 1 bit (em geral, na posição mais à esquerda) para indicar o valor do sinal, e os n-1 bits restantes para indicarem a magnitude do número. Neste tipo de representação, o valor dos bits usados para representar a magnitude (valor absoluto do número) é o mesmo, seja para números positivos ou números negativos, o que varia é o valor do bit de sinal. Esta é uma forma idêntica de representar a que usamos na vida real, com a diferença de que usamos símbolos gráficos no lugar do bit de sinal do computador. Profs. Fábio Lucena Veloso e Roberto Barros 19 Exemplos de uma representação de dados em sinal e magnitude. Representação de valores em sinal e magnitude Características da representação em Sinal e magnitude • Possui duas representações para o ZERO (matematicamente incorreto) o que é uma desvantagem em relação a outros métodos de representação • Representação dos números é simétrica entre os números positivos e negativos, limitada à quantidade permitida de bits dos registradores internos • Se os números forem fracionários, a faixa de representação é mais reduzida. Restringe-se a quantidade de algarismos da parte inteira. Limites de Representação Se os registradores que irão armazenar os valores têm capacidade para receber n algarismos, então a faixa limite de números inteiros, que pode ser armazenada nos referidos registradores é obtida pela expressão: - ( 2n-1 - 1) • • a + ( 2n-1 - 1) Se n é a quantidade limite de algarismos, então a magnitude é calculada a partir de n-1 algarismos, visto que 1 algarismo é reservado para indicar o sinal do número Do valor obtido (2n-1) subtrai-se 1 (para os valores negativos e positivos) porque o primeiro valor a ser representado é zero Ex. a) Para um registrador de 6 bits, n = 6, e os limites de representação serão: De - ( 26-1 - 1) a + ( 26-1 - 1) ou - ( 25 – 1 ) a + ( 25 – 1 ) = - 31 a + 31 b) Para um registrador de 16 bits, n = 16, teremos: De - ( 216-1 - 1) a + ( 216-1 - 1) ou - ( 215 – 1 ) a + ( 215 – 1 ) = - 32767 a + 32767 Profs. Fábio Lucena Veloso e Roberto Barros 20 c) Para um registrador de 16 bits, porém representando um número fracionário tendo, por exemplo, 10 bits para a parte inteira, 5 bits para a parte fracionária e 1 bit para o sinal: Neste caso, n = 10 (somente a parte inteira, é claro) e os limites serão: De - ( 210-1 - 1) a + ( 210-1 - 1) ou - ( 29 – 1 ) a + ( 29 – 1 ) = - 511 a + 511 Considerações sobre representação em Sinal e Magnitude As operações com números negativos realizadas com sinal e magnitude são demoradas e difíceis porque é necessário efetuar várias comparações e decisões em vista da manipulação dos sinais das parcelas para determinar o sinal do resultado. As mesmas operações se tornam mais simples e rápidas quando realizadas através da aritmética de complemento O problema encontrado pelos fabricantes de computadores na implementação da ULA para que efetuasse operações aritméticas com valores representados em sinal e magnitude residiu, principalmente, em dois fatores: a) Custo – devido à necessidade de construção de dois elementos diferentes, um para efetuar somas e outro para efetuar subtrações b) Velocidade – ocasionada pela perda de tempo gasto na manipulação dos sinais, de modo a determinar o tipo de operação e o sinal do resultado Outro fator é a inconveniência da dupla representação do ZERO, o que resulta em um circuito lógico específico para evitar erros de interpretação. No sinal e magnitude, a única diferença entre um valor positivo o mesmo valor negativo é o bit de sinal, enquanto que na forma de complemento a 2, os dois valores são tais que se os somarmos obteremos 0 com um overflow de 1 bit (vai um). Overflow: Diz-se que há overflow quando a soma de dois números de n algarismos der como resultado um número com n+1 algarismos. Este fato é válido, sejam números binários ou decimais, com ou sem sinal. Nenhum sistema moderno emprega aritmética em sinal e magnitude, a qual foi substituída por aritmética em complemento a 2 (complemento a base) Algoritmo para a soma em Sinal e Magnitude 1) Se ambos os números tem o mesmo sinal, somam-se as magnitudes, o sinal de resultado é o mesmo das parcelas; 21 Profs. Fábio Lucena Veloso e Roberto Barros 2) Se o número tem sinais diferentes: a) Identifica-se a maior das magnitudes e registra-se o seu sinal; b) Subtrai-se a magnitude menor da maior (apenas as magnitudes); c) Sinal do resultado é igual ao sinal da maior magnitude Exemplo: Somar os números: (+13) e (+12) +13 +12 +25 0 0 0 0 1 1 0 1 0 1 1 0 0 1 1 0 0 1 Mesmo sinal (bit 0). Somam-se as magnitudes e coloca-se o bit 0 no resultado Somar os números: (-17) e (-9) - 17 - 9 -26 1 1 1 1 0 0 0 1 0 1 0 0 1 1 1 0 1 0 Mesmo sinal (bit 1). Operação idêntica à do exemplo anterior Somar os números: (+18) e (-11) +18 - 11 +7 0 1 0 1 0 0 1 0 0 1 0 1 1 0 0 1 1 1 Sinais diferentes. Maior número (+18), bit de sinal 0. Subtração das magnitudes; bit de sinal 0 no resultado Somar os números: (-21) e (+10) - 21 +10 - 11 1 0 1 1 0 1 0 1 0 1 0 1 0 0 1 0 1 1 Sinais diferentes; maior magnitude (21), sinal (–) (bit 1). Subtrai-se o menor do maior; bit 1 (sinal (-) para o resultado Algoritmo para a subtração em Sinal e Magnitude 1) Troca-se o sinal do subtraendo 2) Procede-se como no algoritmo da soma 22 Profs. Fábio Lucena Veloso e Roberto Barros Exemplo: Efetuar a subtração: (-18) - (+12) (-18) - (+12) = (-18) + (-12) = - 30 - 18 - 12 - 30 1 1 1 1 0 0 1 0 0 1 1 0 0 1 1 1 1 0 Foi trocado o sinal de (+12) para (-12) e a operação passou a ser de soma; como números tem mesmo sinal (-) resultado = (-) Efetuar a subtração: (-27) - (-14) (-27) - (-14) = (-27) + (+14) = -13 - 27 +14 - 13 1 0 1 Foi trocado o sinal de (-14) para (+14) e a operação passou a ser de soma; como números tem sinais deferentes subtrai-se o menor do maior; resultado é igual ao do maior = (-) 1 1 0 1 1 0 1 1 1 0 0 1 1 0 1 Representação de números negativos em complemento O conceito de complemento é válido para qualquer base de numeração. Há dois tipos de complemento: • • Complemento a base Complemento a base menos 1 Complemento a base: O termo complemento, em matemática, significa a quantidade que falta para completar um valor, torná-lo completo Em operações aritméticas, o complemento a base de um número N é o valor necessário para se obter Bn, ou seja: Complemento a base de N = Bn – N, Onde: n = quantidade de algarismos utilizados na operação N = valor do número Na base 10: Na base 8: Na base 2: N = 76310 N = 2548 N = 1102 C10 de N = 105 – N = 10000010 – 76310 = 9923710 C8 de N = 85 – N = 1000008 – 2548 = 775248 C2 de N = 25 – N = 1000002 – 1102 = 110102 Profs. Fábio Lucena Veloso e Roberto Barros 23 (Considerando-se números com cinco algarismos) Na prática: • • 1ª etapa: Subtrair cada algarismo do maior algarismo da base considerada 2ª etapa: Ao resultado encontrado somar 1 ao algarismo menos significativo (mais à direita) Refazendo os exemplos acima: N = 76310 N = 2548 N = 1102 C10 de N = 99999 – 763 = 9923610 + 1 = 9923710 C8 de N = 77777 – 254 = 775238 + 1 = 772548 C2 de N = 11111 –110 = 110012 + 1 = 110102 Complemento a base menos 1 A definição matemática do complemento a base menos um (Cb-1) de um número N é: Cb-1(N) = 2n – N – 1 Onde: n = quantidade de algarismos do número 2n – N = Cb – 1 Cb = complemento a base Pode-se afirmar que se trata da representação em complemento a base subtraída de 1, daí o nome complemento a base menos 1. Na prática: Obtém-se o complemento a base menos 1 de um número (CB-1), subtraindo-se do valor (B-1) cada algarismo do número, B-1 é o maior algarismo de uma base. Se a base for 2, pode-se obter a representação em complemento a base menos 1 através da troca do valor dos bits do número, ou seja, alterar o valor de cada bit 1 para 0 e viceversa. Complemento a base 2 A primeira etapa desse método consiste na operação de obtenção do complemento a base -1. Quando se tratar de valores na base 2 pode-se executar a primeira etapa de obtenção do complemento a 2 de um número, ao invés de substituir cada algarismo do número do maior algarismo da base (no caso 1), simplesmente deve se inverter o valor do algarismo, isto é, se for 0 passar para 1 e vice-versa. Profs. Fábio Lucena Veloso e Roberto Barros 24 A segunda etapa permanece a mesma (somar 1 ao resultado) Exemplos: Quase a totalidade dos computadores modernos utiliza a aritmética de complemento a 2 (quando se trata de ponto fixo) devido a duas grandes vantagens em relação a sinal e magnitude: 1) Possui uma única representação para o ZERO; 2) Necessita de apenas um circuito para realizar as operações de soma e subtração (mais barato) Uma única representação para o ZERO acarreta uma assimetria. Há um número negativo a mais do que números positivos. Exemplo: Um computador com um registrador com capacidade para armazenar 6 bits para o registrador representará o zero da seguinte forma: 000000 Æ o 1º zero indica o sinal do número Para calcular o complemento a 2 desse numero usando o método rápido: 000000 Æ 111111 + 1 = 00000 O vai um para a 7ª ordem é desprezado, porque consideramos o limite de 6 bits para o registrador. O valor final é igual ao inicial, portanto uma única representação para o ZERO! Limites de representação em complemento a 2 Pode-se generalizar para qualquer quantidade de bits nos registradores com a seguinte expressão: - ( 2n-1) a + ( 2n-1 - 1) 25 Profs. Fábio Lucena Veloso e Roberto Barros Exemplo: No caso de um registrador de 6 bits. Teremos: 26 valores representáveis = 64 números binários Utilizando a expressão teremos: de –32 a +31 (+32 não possui representação com esta quantidade de bits) Aritmética com complemento a 2 Aritmética em complemento a 2 A aritmética em complemento a 2 requer apenas um circuito para somar dois números e um circuito que realize a operação de complementação. O algoritmo básico refere-se, então, à soma dos números, considerando-se que os negativos estejam representados em complemento a 2; ele acusa também, se o resultado ultrapassar a quantidade de bits representáveis pela ULA (e registradores), que denominamos overflow. Algoritmo para soma em complemento a 2: 1) somar os dois números, bit a bit, inclusive o bit de sinal 2) desprezar o último “vai 1” (para fora do número), se houver 3) se, simultaneamente, ocorrer “vai 1” para o bit de sinal e para fora do número, ou se ambos não existirem, o resultado está correto. 4) Se ocorrer apenas um dos dois “vai 1” (ou para o bit de sinal ou para fora do número), o resultado está incorreto. Ocorreu overflow. Exemplos: (com registradores de 5 bits) 1) Dois números positivos: N1 + N2 + 9 + (+ 4) = + 13 +9 + (+ 4) 0 1001 0 0100 + 13 0 1101 (1ª parcela) (2ª parcela) bit de sinal (+) note que os bits de sinal da 1ª parcela e da 2ª parcela são ambos 0 e que o bit de sinal da soma é 0, indicando que a soma é positiva. 26 Profs. Fábio Lucena Veloso e Roberto Barros 2) Número positivo e número negativo menor: N1 + C2 de N2 + 9 + (-4) (lembre-se que -4 estará em sua forma de complemento 2 = 11100) +9 + (-4) 0 1001 1 1100 +5 1 0 0101 bit de sinal (+) este “vai 1” é desprezado neste caso, o bit de sinal da 2ª parcela é 1. Note que os bits de sinal também participam do processo de adição. De fato, um “vai 1” é gerado na última posição da adição. Este “vai 1” é desprezado, de modo que o resultado final da soma é 00101 que é equivalente a + 5. 3) Número positivo e número negativo maior: C2 de N1 + N2 -9 + (+ 4) (lembre-se que -9 estará em sua forma de complemento 2 = 10111) -9 + (+ 4) 1 0111 0 0100 -5 1 1011 complemento 2 de 1011 = 0101 = 5 bit de sinal (-) A soma aqui tem um bit de sinal igual a 1, indicando um número negativo. Como a soma é negativa, ela está na forma de complemento a 2, de modo que os quatro últimos bits (1011) representam o complemento a 2 de 0101 (equivalente ao decimal 5). Desta forma, 11011 é o equivalente a -5, o resultado correto. 4) Dois números negativos: C2 de N1 + C2 de N2 -9 + (-4) (lembre-se que -9 estará em sua forma de complemento a 2 = 10111 e que -4 estará em sua forma de complemento a 2 = 11100) -9 -4 -13 1 0111 1 1100 1 1 0011 complemento 2 de 0011 = 1101 = 13 bit de sinal (-) este “vai 1” é desprezado 27 Profs. Fábio Lucena Veloso e Roberto Barros Exemplos: (com registradores de 6 bits) 5) N1 + N2: + 12 + 7 + 12 + 7 001100 000111 + 19 010011 bit de sinal (+) 6) N1 + C2 de N2 + 12 + (-7) + 12 - 7 001100 111001 + 5 1000101 bit de sinal (+) este “vai 1” é desprezado 7) C2 de N1 + N2 -12 + 7 -12 +7 110100 000111 -5 111011 complemento 2 de 11011 = 00101 = 5 bit de sinal (-) 8) C2 de N1 + C2 de N2 -12 + (-7) -12 + (-7) -19 110100 111001 1 101101 complemento 2 de 01101 = 010011 = 19 bit de sinal (-) este “vai 1” é desprezado 28 Profs. Fábio Lucena Veloso e Roberto Barros Subtração no sistema de complemento de 2: A operação de subtração usando o sistema de complemento de 2, na verdade, envolve a operação de adição e não é, realmente, diferente dos casos vistos anteriormente. Ao se subtrair um número binário (o subtraendo) de outro número binário (o minuendo), o procedimento é o seguinte: 1) Tome o complemento 2 do subtraendo, incluindo o bit de sinal. Se o subtraendo for um número positivo, isto irá torná-lo um número negativo na forma de complemento 2. Se o subtraendo for um número negativo, isto irá torná-lo um número positivo na forma binária verdadeira. Em outras palavras, nós estamos mudando o sinal. 2) Depois de tomar o complemento 2 do subtraendo, ele é somado ao minuendo. O minuendo é mantido em sua forma original. O resultado dessa adição representa a diferença perdida. O bit de sinal dessa diferença determina se é + ou – e se está na forma binária verdadeira ou na forma de complemento 2. Exemplos: (com registradores de 5 bits) 1) Vamos considerar o caso: + 4 deve ser subtraído de + 9 Minuendo = + 9 Subtraendo = + 4 +9 - (+ 4) +5 complemento 2 = 11100 01001 11100 complemento 2 = 11100 1 00101 “vai 1” desprezado 2) + 9 – (- 4) +9 - (- 4) 01001 00100 + 13 01101 complemento 2 de - 4 = 11100 ; complemento 2 de 11100 = 00100 3) - 9 - (+ 4) -9 - (+ 4) - 13 10111 11100 1 10011 complemento 2 = 11100 complemento 2 de 0011 = 1101 = 13 bit de sinal (-) “vai 1” desprezado Profs. Fábio Lucena Veloso e Roberto Barros 29 4) - 9 - (- 4) -9 - (- 4) 10111 00100 -5 11011 complemento 2 de - 4 = 11100 ; complemento 2 de 11100 = 00100 complemento 2 de 1011 = 0101 = 5 bit de sinal (-) Obs. Lembrar sempre: a) As operações de soma são normalmente realizadas como soma b) As operações de subtração são realizadas como soma de complemento c) Se o resultado encontrado é um valor positivo, então o valor decimal correspondente da magnitude é obtido por pura conversão de base 2 para 10 d) Se o resultado encontrado é um valor negativo, deve-se primeiro converter esse valor para representação de sinal e magnitude e, em seguida, converter a magnitude para complemento a 2 e depois converter o resultado da base 2 para 10. Finalizando põem-se o sinal negativo no resultado. Representação em ponto flutuante. Em aplicações científicas ocorrem números muito grandes (como na astronomia) ou muito pequenos (como na física atômica). Nesses casos, sempre se estará interessado na ordem de grandeza do valor, e um certo número de dígitos de precisão. Nas máquinas de calcular científicas vemos a representação de números em notação científica, onde 10234 é mostrado como 1.0234E05, significando 1.0234 * 105. Em computação, chamamos a notação científica de representação em ponto flutuante. Um número em notação científica é representado por um produto de dois fatores: N = ± FxB ± E Onde: • N = número que se deseja representar • ± = sinal do número • F = dígitos significativos do número (também chamada parte fracionária ou mantissa) • B = base de exponenciação • ± E = valor do expoente, com seu sinal (expoente pode ser positivo ou negativo) Profs. Fábio Lucena Veloso e Roberto Barros 30 Exemplo: -2,3456 x 10-23 A representação interna dos números de ponto flutuante varia de fabricante para fabricante, pois além de se poder representar a mantissa de números negativos na forma sinal e magnitude ou complemento a dois, o expoente também pode ter essa representação, ou, como é o caso dos mainframes IBM, ter a representação de excesso a N. No caso do IBM, um expoente 64 significa 0, enquanto o 63 significa -1 e o 67 significa 3 (excesso a 64). Também a base da exponenciação pode ser 2, 10 ou 16 (No IBM a base é 16). O importante é saber de alguns problemas que influenciam na programação com números de ponto flutuante. O resultado de operações aritméticas deve ser sempre considerado como aproximado. Com isso, nunca se deve perguntar se uma variável é igual a outra, ou igual a uma constante. Em vez disso, deve ser perguntado se a diferença entre os valores é muito pequena. Por exemplo, para saber se um número é zero, usa-se: IF ABS(NUMERO) < 0.0000001. Para saber se um número é igual a outro pergunta-se: IF ABS (NUMEROOUTRO)<0.0001. Apesar de um número poder ser muito grande ou muito pequeno, não se deve somar números de grandeza muito diferentes, pois o menor número não causará diferença na soma, devido à pequena precisão dos dados. Exemplo: 1000000000000 + 0,000000001 = 1000000000000, uma vez que o número de dígitos significativos é maior que a precisão da variável que contém o número. Em COBOL, os números de ponto flutuante são declarados como COMP-1 (precisão simples- 4 bytes) e COMP2 (precisão dupla- 8 bytes). Na linguagem C e no Visual Basic os tipos podem ser: SINGLE (4 bytes -3.402823E38 até -1.401298E-45 para números negativos; 1.401298E-45 até 3.402823E38 para positivos) e DOUBLE (8 bytes -1.79769313486232E308 até -4.94065645841247E-324 para negativos; 4.94065645841247E-324 até 1.79769313486232E308 para positivos). Em ponto flutuante, várias configurações podem representar o mesmo valor. Por exemplo, 102E1 = 1,02E3 = 0,102E4. A representação aonde o dígito antes da vírgula é zero, e o após a vírgula é não zero é chamada de representação normalizada. Representação decimal As representações em ponto fixo e ponto flutuante são ótimas para cálculos matemáticos, em aplicações científicas. Para aplicações comerciais, como contabilidade e sistemas financeiros, precisa-se de um sistema que não tenha problemas de precisão, nem de conversão, que aparecem nas conversões de números decimais fracionários para binário (por exemplo, 0,1 na base 10 é uma dízima nas bases 2 e 16). A solução encontrada para equilibrar a necessidade de realizar eventuais operações aritméticas com valores decimais, mas representá-los internamente, sempre de forma binária, constituiu-se um método híbrido de representação de dados, denominado código binário decimal (Binary Coded Decimal – BCD) 31 Profs. Fábio Lucena Veloso e Roberto Barros O sistema BCD (código binário decimal) é um misto de decimal com binário, na forma BCD os dados decimais usados em um programa, em vez de serem diretamente convertidos da base 10 para a base 2, são representados internamente por códigos binários correspondentes a cada algarismo decimal, conforme mostrado na figura. Decimal 0 1 2 3 4 5 6 7 8 9 Binário BCD 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 Assim, por exemplo, o número decimal 7458 seria representado no código BCD da seguinte forma: 0111 ( 7) 0100 ( 4) 0101 ( 5) 1000 ( 8) Entre os algarismos sem código válido em decimal (códigos representativos dos valores decimais de 10 a 15) é comum utilizar alguns deles para indicar o sinal do número. Por exemplo, alguns sistemas utilizam 1100 para representar o sinal de (+) e 1101 para representar o sinal de (-). Atualmente só é usado o sistema conhecido como decimal compactado. Nesse sistema, a cada 4 bits têm-se um dígito decimal, valendo de 0 a 9. As configurações de A a F só são usadas no dígito mais à direita, para representar o sinal (B e D representam o -, enquanto A, C, E e F representam o +). Nos mainframes IBM, os números em decimal compactado podem ter até 16 bytes de comprimento, permitindo números com até 31 dígitos de precisão, já que o sinal gasta meio byte. Nos computadores aonde a CPU não tem instruções para operar com números nessa representação, é necessária a implementação de rotinas para efetuar as 4 operações com esses números. Também se pode implementar a aritmética decimal compactada nos co-processadores aritméticos, para economizar tempo de execução de programas. Execução de Programas Para que um computador execute alguma tarefa, é necessário que os passos correspondentes estejam especificados um a um na sua memória RAM, em códigos que Profs. Fábio Lucena Veloso e Roberto Barros 32 a sua CPU possa interpretar. Chamamos a esses códigos instruções de máquina, e ao conjunto de instruções, programa em linguagem de máquina. Como esta linguagem é apropriada para o entendimento pela CPU, mas não o é para o entendimento pelas pessoas, é necessário que existam programas que convertam instruções inteligíveis pelo homem para a linguagem de máquina. Vamos nesse capítulo abordar algumas linguagens de programação existentes, e os processos de executar os programas escritos em uma linguagem de alto nível em uma CPU. Linguagem de Programação É uma linguagem criada, para instruir um computador a realizar suas tarefas. Um programa escrito em uma linguagem de programação é freqüentemente denominado código. Codificar um algoritmo significa converter suas declarações em comandos em instruções específicas usando a sintaxe específica de cada linguagem de programação. Existem muitas linguagens de programação disponíveis para uso em computadores. Algumas, como o COBOL, são para uso comercial, e estão disponíveis na maioria dos computadores. Outras são para uso específico em certo tipo de equipamentos, e não estão disponíveis para uso em outras plataformas, como o Visual Basic, que não tem versões disponíveis para Main Frames, sendo seu uso restrito a computadores pessoais ou estações de trabalho. Classificação das Linguagens de Programação 1) Quanto ao nível, as linguagens podem ser a) de máquina: O tipo mais primitivo de linguagem de programação é a linguagem de máquina que foi utilizada pela primeira geração de computadores. Um programa em linguagem de máquina é uma seqüência de números que representam instruções e os dados a serem manipulados. b) de baixo nível (Assembly) O Assembly é conhecido também como linguagem de montagem, e os programas que traduzem o código para linguagem de máquina são chamados de montadores. Os que traduzem as linguagens de alto nível são chamados de compiladores. As linguagens Assembly são muito semelhantes à linguagem de máquina, tendo o programador que codificar as instruções que a CPU conhece uma a uma, apenas utilizando-se de mnemônicos para os códigos de instrução (por exemplo ADD em vez de 45, para instruir a CPU a fazer uma soma), e endereços simbólicos para os dados (por exemplo NOME-CLI em vez de 124503 para representar o endereço onde está armazenado o nome do cliente na memória). c) alto nível (COBOL, BASIC, C, ETC). As linguagens de alto nível se utilizam instruções que necessitam às vezes de muitas instruções de máquina para a sua realização. Por exemplo, X = SQRT(A) executa um algoritmo complexo para guardar em X a raiz quadrada de A. Profs. Fábio Lucena Veloso e Roberto Barros 33 2) Quanto à geração, as linguagens podem ser classificadas como de: a) primeira geração (programação no painel dos primeiros computadores), b) segunda geração (Assembly), c) terceira geração (linguagens procedimentais, como COBOL, C, FORTRAN) e d) quarta geração (linguagens para utilização até por usuários finais, como SQL, QBE, e outras). 3) Quanto ao uso, as linguagens podem ser classificadas como: • • • científicas (FORTRAN), comerciais (COBOL), e de uso geral ( PL/I; BASIC) Principais linguagens de terceira geração: a) FORTRAN - FORmula TRANslation - Utilizada para cálculos científicos b) ALGOL - ALGOrithm Language c) COBOL - COmmon Business Oriented Language d) LISP - Linguagem para manipulação de listas e) PL/I - Programing Language I - Linguagem da IBM, com características comerciais, Científicas e Algoritmica. Desenvolvida para substituir todas as demais. f) PASCAL - Linguagem de uso geral, desenvolvida em meios acadêmicos. A última versão desta linguagem é o DELPHI, versão visual do PASCAL orientado a objetos. g) C - Linguagem de alto nível, com funções de baixo nível. Usada como substituto dos Assembly, com a vantagem de ser multiplataforma. A última versão desta linguagem é o Visual C++, versão visual do C++, que é o C orientado a objetos. h) BASIC - Beginners All purpose Symbolic Instruction Code. Linguagem poderosa, desenvolvida para ser usada por principiantes, por isso mesmo muito simples. A versão mais moderna desta linguagem é o Visual Basic, para Windows. O VB5 é apenas para Windows 95, enquanto o VB4 pode gerar executáveis para Windows de 16 ou 32 bits (Windows 3.1 ou Windows 95). Profs. Fábio Lucena Veloso e Roberto Barros 34 Montagem Apesar do usuário escrever os programas (instruções) por intermédio de forma simbólica com as linguagens de programação, o computador continua entendendo somente o código binário havendo, portanto, necessidade de conversão ou tradução da linguagem simbólica para a linguagem binária executável. A tradução mais rápida que existe denomina-se montagem e é realizada por um programa denominado montador (assembler). A montagem é realizada para traduzir um programa em linguagem de montagem para seu equivalente em linguagem de máquina (executável). Fluxo básico de uma montagem O programa escrito em linguagem de montagem é chamado de código fonte e tem suas instruções examinadas uma a uma e em seguida convertidas para outro programa em linguagem binária denominado código objeto. Funções do Montador a) Substituir códigos de operação simbólicos por valores numéricos; b) Substituir nomes simbólicos de endereços pelos valores numéricos dos endereços; c) Reservar espaço em memória para armazenar as instruções e dados; d) Converter valores de constantes para código binário e e) Examinar a correção de cada instrução. Profs. Fábio Lucena Veloso e Roberto Barros 35 Compilação O método chamado compilação é usado quando se pretende converter para linguagem de máquina um programa escrito em linguagem de mais alto nível do que linguagem de montagem. É o processo de análise de um programa escrito em linguagem de alto nível (código fonte) e sua conversão (ou tradução) em um programa equivalente descrito em linguagem binária executável.(código objeto) O programa que executa esta tarefa é chamado de compilador. Fluxo básico do processo de compilação Análise do processo O processo é semelhante ao da montagem (fonte-objeto), porém mais complexo e demorado. Durante a compilação o código fonte é analisado, comando a comando e dividido em três partes funcionais distintas: 1) Análise léxica: que consiste em decompor o programa fonte em seus elementos individuais distintos para serem verificados de acordo com as regras da linguagem; Profs. Fábio Lucena Veloso e Roberto Barros 36 2) Análise sintática: consiste na criação das estruturas de cada comando, na verificação da correção dessas estruturas e na alimentação da tabela de símbolos, tudo de acordo com as regras gramaticais de cada linguagem; 3) Análise semântica: consiste na verificação das regras de semântica da linguagem produzindo mensagens de erro para as incorreções e inconsistências. Ligação ou Linkedição O programador não necessita codificar certas operações porque o código binário para muitas tarefas já está armazenado no sistema. É preciso apenas que este código seja buscado onde estiver e incorporado ao código fonte. As rotinas externas ao programa são organizadas em arquivos e diretórios específicos e são usualmente chamados de bibliotecas (library). Quando o programa deseja usar estas bibliotecas, insere uma chamada em seu código (CALL). Para que um programa seja executado é necessário incorporar ao código objeto as rotinas existentes em bibliotecas. Cria-se uma conexão lógica entre o código objeto principal e o código objeto da rotina. Esse processo é denominado de ligação ou linkedição e é realizado por um programa conhecido por ligador ou linkeditor. Fluxo básico do processo de ligação O fluxo básico do processo de execução de um programa inclui duas etapas: 1) Compilação 2) Ligação ou linkedição Profs. Fábio Lucena Veloso e Roberto Barros 37 Como o resultado, obtém-se um conjunto de códigos de máquina interligados e prontos para a execução. Análise do processo de ligação O funcionamento do linkeditor consiste em examinar todo o código objeto gerado após a compilação e procurar referências externas não resolvidas (encontradas nas bibliotecas). Ao encontrar, substitui a linha do chamado pelo código objeto da rotina. Na prática, os processos de compilação e linkedição são fases distintas e independentes do procedimento de execução de um programa, geram códigos distintos: • • Código objeto, ao final da compilação e Código executável após a linkedição Em ambos os casos, os códigos são armazenados em memória secundária. Ao testarmos o código fonte estaremos testando os erros de codificação na linguagem específica e ao testarmos o código executável verificamos a correção lógica do sistema utilizando-se de uma massa de dados de teste. Há um tipo de linkeditor, chamado de loader (carregador), que não armazena código executável, sendo um pouco mais rápido.Realiza em seqüência imediata as duas tarefas: ligação e execução do código de máquina, sem gerar código executável, cria o código, mas não armazena. 38 Profs. Fábio Lucena Veloso e Roberto Barros Podemos identificar, finalmente, três fases no processo: 1) Compilação 2) Ligação 3) Execução Interpretação O método de interpretação se caracteriza pela realização das três etapas (compilação, ligação e execução), comando a comando, do programa fonte. Não há processo explícito de compilação e ligação. O programa fonte é diretamente executado (interpretado) por outro programa chamado interpretador produzindo o resultado. Não há código objeto ou código executável. Em resumo, cada comando do código fonte é lido pelo interpretador e convertido em código executável e imediatamente executado antes que o comando seguinte seja lido. Compilação x Interpretação Ao analisarmos as vantagens e desvantagens entre os métodos de compilação e interpretação podemos resumir nas seguintes: a) A principal vantagem da interpretação sobre a compilação é a capacidade de identificar e indicar um erro na lógica do algoritmo ou na consistência entre o valor e o tipo de dado; b) A desvantagem da interpretação é o consumo de memória. O compilador só permanece na memória durante a fase de compilação enquanto que o interpretador tem de estar presente durante toda a execução; c) Outra desvantagem da interpretação consiste na possibilidade de certas partes do código fonte terem de ser interpretadas tantas vezes quantas forem definidas(uma instrução de looping por exemplo) Veja o quadro comparativo que resume as considerações relativas ao consumo de recursos de computador com os dois processos. Recursos Uso da memória (durante a execução) • Interpretador ou Compilador • Código Fonte • Código Executável • Rotinas de Bibliotecas Instruções de Máquina (durante a execução) • Operações de Tradução • Ligação de Bibliotecas • Programa de Aplicação Compilação Interpretação Não Não Sim Só as necessárias Sim Parcial Parcial Todas Não Não Sim Sim Sim Sim 39 Profs. Fábio Lucena Veloso e Roberto Barros Arquitetura RISC RISC é uma abreviatura de Reduced Instruction Set Computer, computador com conjunto reduzido de instruções. O desenvolvimento de sistemas com arquitetura RISC começou no fim da década de 70 na Universidade de Stanford e no início dos anos 80 na IBM. Os cientistas entendiam que os programas não estavam fazendo o uso mais eficiente possível do hardware. Diversos estudos foram realizados a fim de verificar o comportamento das instruções de máquina em relação ao desempenho dos sistemas na execução de programas escritos em linguagens de alto nível. Cada linguagem possuía alguns comandos poderosos criados com a finalidade de facilitar a vida dos programadores mais do que facilitar o desempenho da computação, isto é, facilitar o processamento do hardware. Permitia aos programadores desenvolver programas mais complexos com menor esforço, pois os comandos disponíveis realizavam muitas tarefas. Como as instruções de máquina continuaram primitivas, um único comando de alto nível tinha que ser convertido em várias instruções de máquina. Como havia acentuada separação entre as linguagens de alto nível e a de máquina (gap semântico), os compiladores tornaram-se complexos para permitir a perfeita conversão das linguagens resultando um número excessivo de instruções e ineficiência na execução dos programas. A arquitetura CISC aumentou a quantidade de instruções de máquina, aperfeiçoando alguma delas para atender os requisitos do processamento de um comando complexo, incluiu mais modos de endereçamento no conjunto de instruções e utilizou-se de microprogramação para certas operações por firmeware. Em conclusão aos estudos realizados, no sentido de otimizar o desempenho do hardware, chegou-se a uma estrutura de máquina que permitia minimizar a necessidade de um grande número de instrução sendo que percentualmente algumas delas ocorriam com pequena freqüência. Sistemas Tipo Ano Qtde. Instr. Qtde. Reg. Tamanho Instr IBM /370-168 CISC 1973 208 16 16 - 48 bits Intel 80846 Intel Pentium CISC RISC 1989 1993 147 150 8 8 1 - l7 bits 1 - l7 bits Power PC 601 Sparc 10 Alpha 21064 RISC RISC RISC 1993 1987 1992 184 52 125 32-1 32-PF até 528 32-1 32-PF 32 bits 32 bits 32bits 40 Profs. Fábio Lucena Veloso e Roberto Barros A existência de muitas instruções significa muitos códigos de operação e muitos bits em cada código e ainda a dupla desvantagem de se ter instruções com maior comprimento, devido ao número de bits do Código da Operação e mais o tempo de sua interpretação. Baseado nestas conclusões surgiram os primeiros protótipos de máquinas RISC. Veja a seguir, o quadro comparativo da freqüência de ocorrência de certos comandos de linguagem de alto nível na execução de programas. Comando Assingn Loop Call If Goto Outros Ocorrência Pascal C 45% 38% 5% 3% 15% 12% 29% 43% 3% 6% 1% Peso nas Instr. De Máquina Pascal C 23% 13% 42% 32% 31% 33% 11% 21% 3% 1% Peso em Ref. à MP Pascal C 14% 15% 33% 26% 44% 45% 7% 13% 2% 1% Características da Arquitetura RISC Menor quantidade de instruções A característica mais marcante da arquitetura RISC é a de possuir um conjunto de instruções menor que o das máquinas CISC de mesma capacidade. Com menor número de instruções e com cada uma delas tendo sua execução otimizada, o sistema de vê produzir os resultados com melhor desempenho apesar de determinar a confecção de programas mais longos. Execução otimizada da chamada de funções As chamadas de funções que consomem razoável tempo do processador requerem poucos dados, mas consomem, na transferência, demorados acessos a memória em leituras e escritas. A arquitetura RISC possui mais registradores permitindo que os parâmetros e variáveis sejam manuseados na própria CPU e não na MP como nas máquinas CISC. Esta característica permite melhor desempenho do processamento já que executa mais otimizadas as chamadas de funções. Menor quantidade de modos de endereçamento Nas máquinas CISC as instruções podem ser realizadas com operandos localizados por diversos modos de endereçamento, como já vimos. Nas máquinas RISC criou-se um modo geral de apenas dois tipos de instrução para localização de operandos: LOAD/STORE, utilizando o acesso à memória somente pelo modo direto e as demais operações, no processador (matemáticas e lógicas). Houve simplificação do projeto e da implantação das instruções, reduzindo os ciclos de relógio necessários a sua realização. Profs. Fábio Lucena Veloso e Roberto Barros 41 Modo de execução Pipelining Chama-se de pipelining o método no qual os processadores executam várias instruções quase que totalmente em paralelo acelerando o desempenho dos processadores e reduzindo o tempo de execução para poucos ciclos. A partir dos processadores Intel 8086/8088 as máquinas CISC empregam este método. O objetivo do projeto de uma máquina RISC é o da execução de uma instrução a cada ciclo de relógio utilizando o pipelining em mais larga escala. A seguir, quadro resumo das características da arquitetura RISC Característica Menor quantidade de instruções que as máquinas CISC Execução otimizada de chamada de funções Menor quantidade de modos de endereçamento Utilização em larga escala do pipelining Considerações Simplifica o processamento de cada instrução e torna este item mais eficaz Embora o processador RS/6000 possua 184 instruções, ainda assim é bem menos que as 303 instruções dos sistemas VAX-11. Além disso, a maioria das instruções é realizada em 1 ciclo de relógio, o que é considerado o objetivo maior dessa arquitetura As máquinas RISO utilizam os registradores da UCP (em maior quantidade que os processadores OISO) para armazenar parâmetros e variáveis em chamadas de rotinas e funções. Os processadores OISO usam mais a memória para a tarefa As instruções de processadores RISO são basicamente do tipo Load/Store, de desvio e de operações aritméticas e lógicas, reduzindo com isso seu tamanho A grande quantidade de modos de endereçamento das instruções de processadores CISC aumenta o tempo de execução das mesmas Um dos fatores principais que permite aos processadores RISO atingir seu objetivo de completar a execução de uma instrução pelo menos a cada ciclo de relógio é o emprego de “pipelining” em larga escala Profs. Fábio Lucena Veloso e Roberto Barros 42 CISC x RISC Na avaliação das duas arquiteturas encontramos adeptos que poderão apontar vantagens e desvantagens de uma ou outra. Muitos são os temas que podem ser abordados na discussão sobre RISC x CISC, um dos quais se refere ao desempenho do processador. Verifica-se que os programas de teste (benchmarks) possuem uma série de complicações na interpretação de seus resultados em função do tipo de ambiente e da natureza dos testes. Os adeptos da arquitetura CISC afirmam que instruções mais complexas redundam em código objeto menor reduzindo o consumo de memória com reflexos no custo do sistema. Isto não é necessariamente correto se considerarmos que uma menor quantidade de instruções nem sempre acarreta menor quantidade de bits, e na realidade o que consome menos memória e menor custo é a quantidade de efetiva de bits que o código apresenta. Uma grande quantidade de operandos para definir instruções em programas em máquinas CISC poderá determinar um número maior de bits do que em programas em máquinas RISC, apesar destes possuírem maior quantidade de instruções. Outro ponto a ser analisado se refere à rapidez de execução de um programa. Os adeptos da CISC afirmam que as máquinas executam mais rapidamente os programas em linguagem de alto nível devido a um menor código binário o que não pode ser de todo correto, pois o tempo de execução de cada instrução pode determinar uma velocidade de execução maior, independente do tamanho do código. Fatores que permitem uma tendência às máquinas RISC de executarem as instruções mais rápido: • • Instruções com C.Op. com menos quantidade de bits e com tempo de codificação menor do que as máquinas CISC; Instruções executadas diretamente pelo hardware e não por microprogramação. Mesmo que a microprogramação acarrete em flexibilidade, adiciona a carga de interpretação de cada instrução Processadores RISC são otimizados para realizar operações de uma tarefa por ciclo de relógio proporcionado por uma grande quantidade de registradores e a grande quantidade de estágios de pipelining.