Para introduzirmos as noções básicas de como funciona um computador, empregaremos um modelo imaginário hipotético que denominaremos de computador hipo. O funcionamento desse modelo tem as características básicas do funcionamento de um computador real embora seja muito mais simples e didático.
Inicialmente vamos apresentar as unidades fundamentais de um computador:
Para descrever o funcionamento do HIPO, vamos dar as regras básicas de funcionamento do componente principal, a CPU. As regras são:
Dadas as regras básicas, precisamos carregar um programa na memória e fazer com que o apontador de instruções aponte para a primeira instrução do programa. Observação: "carregar'' um programa significa colocar cada instrução do mesmo em uma posição de memória. Feito isso, podemos acionar a CPU fazendo com que ela siga as regras básicas e execute o programa dado.
Pergunta: o que aconteceria se trocássemos as regras 2 e 3 de lugar?
Infelizmente o HIPO não reconhece frases do tipo "copie o conteúdo do Acumulador para o Endereço 30''. Por isso precisamos escrever numa linguagem que este possa entender, i.e. em linguagem de máquina. Para este computador, uma instrução será codificada por um inteiro. Por convenção todas as instruções serão codificadas por inteiros positivos (i.e. com sinal +).
Suponha que temos que passar para a "linguagem do HIPO'' a instrução "Copie o conteúdo do Acumulador para o Endereço 30":
Em primeiro lugar, utilizamos o sinal +. Os dois primeiros algarismos correspondem à instrução, ou seja, à operação a ser executada pela CPU. Na seção 4 listamos os códigos de todas as instruções do HIPO. O código da instrução "copie o conteúdo do acumulador no endereço EE'' é o número 12.
Os dois últimos algarismos do código correspondem ao endereço de uma posição de memória, que neste caso é o 30. Logo a instrução acima é codificada, em linguagem de máquina, como
+1230.
Vamos a um outro exemplo: A instrução é "Se o conteúdo do Acumulador for menor que zero, desvie para o endereço 11".
Portanto a codificação desta instrução na linguagem HIPO é +5411.
Cada instrução é composta de um código mais um endereço relativo a esse código, da seguinte maneira:
+IIEE
Onde II é o código de uma das instruções abaixo, e EE é o endereço ao qual ela se refere (00 £ EE £ 99). A seguir, [EE] indica o conteúdo de EE, [AC] indica o conteúdo do acumulador, AI indica o apontador de instrução e (XXX) indica o código simbólico abreviado.
11: (CEA) Copie o conteúdo do endereço EE no acumulador. (AC recebe [EE]).
12: (CAE) Copie o conteúdo do acumulador no endereço EE. (EE recebe [AC])
21: (SOM) Some o conteúdo do endereço EE com o conteúdo do acumulador e guarde o resultado no acumulador. (AC recebe [AC] + [EE])
22: (SUB) Subtraia o conteúdo do endereço EE do conteúdo do acumulador e guarde o resultado no acumulador. (AC recebe [AC] - [EE])
23: (MUL) Multiplique o conteúdo do endereço EE com o conteúdo do acumulador e guarde o resultado no acumulador. (AC recebe [AC] * [EE])
24: (DIV) Divide o conteúdo do acumulador pelo conteúdo do endereço EE e guarde o resultado no acumulador. (AC recebe [AC] / [EE])
25: (MOD) [AC] recebe o resto da divisão [AC] / [EE].
31: (LER) Leia um número e guarde-o no endereço EE. (EE recebe o valor lido)
41: (IMP) Imprima o conteúdo do endereço EE.
50: (NOP) Nenhuma operação é efetuada.
51: (DES) Desvie a execução para o endereço EE, i.e. AI recebe EE.
52: (DPO) Se o conteúdo do acumulador for maior do que zero, desvie a execução para o endereço EE. (Se [AC] > 0, AI recebe EE).
53: (DPZ) Se o conteúdo do acumulador for maior ou igual a zero, desvie a execução para o endereço EE. (Se [AC] ³ 0, AI recebe EE).
54: (DNE) Se o conteúdo do acumulador for menor do que zero, desvie a execução para o endereço EE. (Se [AC] < 0, AI recebe EE.)
55: (DNZ) Se o conteúdo do acumulador for menor ou igual a zero, desvie a execução para o endereço EE. (Se [AC] £ 0, AI recebe EE).
56: (DDZ) Se o conteúdo do acumulador for diferente de zero, desvie a execução para o endereço EE. (Se [AC] ¹ 0, AI recebe EE).
57: (DZZ) Se o conteúdo do acumulador for igual a zero, desvie a execução para o endereço EE. (Se [AC] = 0, AI recebe EE).
58: (DDF) Se o conteúdo do acumulador for diferente de infinito, desvie a execução para o endereço EE. (Se [AC] ¹ INF, AI recebe EE).
59: (DFF) Se o conteúdo do acumulador for infinito, desvie a execução para o endereço EE. (Se [AC] = INF, AI recebe EE).
61: (ADE) Desloque os dígitos do acumulador uma posição à esquerda, desprezando o digito mais significativo.
62: (ADD) Desloque os dígitos do acumulador uma posição à direita, desprezando o digito menos significativo.
70: (PAR) Pare a execução do programa. OBS.: Esta instrução deve ser executada para encerrar a execução do programa.
Dada uma seqüência de números inteiros não negativos, imprimir a sua soma. A seqüência é seguida de um número negativo. Exemplo de seqüência de entrada: +0100, + 0015, +0000, +0007, -0002.
|
|
Linguagem |
Linguagem |
Copie o conteúdo do endereço 30 no acumulador |
01 |
+1130 |
CEA zero |
Copie o conteúdo do acumulador no endereço 40 |
02 |
+1240 |
CAE soma |
Leia um número e coloque no endereço 50 |
03 |
+3150 |
leia: LER num |
Imprima o conteúdo do endereço 50 |
04 |
+4150 |
IMP num |
Copie o conteúdo do endereço 50 no acumulador |
05 |
+1150 |
CEA num |
Se o conteúdo do acumulador for menor que zero, |
06 |
+5411 |
DNE fim |
Copie o conteúdo do endereço 40 no acumulador |
07 |
+1140 |
CEA soma |
Some o conteúdo do endereço 50 |
08 |
+2150 |
SOM num |
Copie o conteúdo do acumulador no endereço 40 |
09 |
+1240 |
CAE soma |
Desvie para o endereço 03 |
10 |
+5103 |
DES leia |
Imprima o conteúdo do endereço 40 |
11 |
+4140 |
fim: IMP soma |
Pare |
12 |
+7000 |
PAR |
|
30 |
+0000 |
zero: |
|
40 |
|
soma: |
|
50 |
|
num: |
A quarta coluna da tabela acima representa o programa escrito em uma linguagem de montagem ou assemby. Escrever um programa em assembly é bem mais fácil que escrevê-lo diretamente em linguagem de máquina. Poderíamos inclusive escrever um programa montador, ou assembler, que se encarregue de traduzir assembly, para linguagem de máquina (i.e. um programa que gere o conteúdo da terceira coluna a partir do conteúdo da segunda e da quarta colunas).
Damos a seguir uma outra representação simbólica do programa 1, a de diagrama de blocos, ou fluxograma. Seu significado deve ser evidente.
End. |
Rótulo |
Assembly |
Fluxograma |
|
|
|
|
Apesar dos computadores só lidarem com números, eles podem armazenar informações que, a princípio, não são números. Por exemplo, como representaríamos a letra "A" no computador ? Como guardaríamos um nome (por ex. "CARLOS DA SILVA")? Podemos representar objetos como números, desde que tenhamos uma tabela de tradução que diga qual número é associado a qual objeto. Vamos ver como representar caracteres na memória do computador : instalamos no computador uma tabela assim:
A = +0001, B = +0002,..., Z = +0026, 0 = +0027, 1 = +0028, ... , 9 = +0036, a = +0037, b = +0038, ...
Daí, podemos "traduzir" qualquer caractere para um número e colocá-lo na memória do computador.
Como faremos para representar seqüências de caracteres? A maneira mais simples é colocar cada letra em uma posição da memória em seqüência, por exemplo :
"CARLOS" ficaria assim :
C,A,R,L,O,S = +0003, +0001, +0018, +0012, +0015, +0019
Vamos agora discutir alguns detalhes adicionais sobre a representação de números inteiros.
Em primeiro lugar notemos que há duas maneiras possíveis de representar o número zero, a saber: +0000 e -0000. Por convenção, fica estabelecido que zero é representado por +0000.
Em segundo lugar falta estabelecer o resultado de uma operação aritmética entre dois inteiros cujo resultado não pode ser expresso como um inteiro. Por exemplo se somarmos +9991 e +0010, o "resultado" (10001) não é mais representável como um inteiro (pois inteiros sendo constituídos de um sinal e 4 algarismos decimais só podem representar números inteiros entre -9999 e +9999). Casos deste tipo são chamados de overflow (transbordamento). Neste caso convencionamos que a CPU retorna o código -0000, que fica sendo nossa representação de infinito (INF), ou de qualquer número inteiro menor que -9999 ou maior que +9999. Também por convenção, o resultado de qualquer operação aritmética envolvendo INF tem como resultado INF; e para as operações de desvio, INF (-0000) não é igual, nem maior, nem menor que zero (+0000).
(-9920 - 80) + 5000 = INF + 5000 = INF.
(-9920 + 5000) - 80 = -4920 - 80 = -5000.
Ao multiplicarmos dois inteiros de 4 dígitos (não nulos), teremos sempre overflow! Isto motiva um modelo melhorado do HIPO, o HIPO-II. No HIPO-II o acumulador tem "precisão dupla", i.e. o acumulador pode conter um número representado por um sinal ( + ou - ) seguido de 8 algarismos decimais (por exemplo, +00000002, +12345678, -12345678). Doravante denominaremos um sinal e 8 algarismos decimais de inteiro longo. Repare que qualquer produto de 2 inteiros pode ser representado como um inteiro longo.
Ao copiarmos o conteúdo do endereço EE no acumulador (instrução CEA), o sinal e os 4 dígitos menos significativos (à direita) de AC recebem [EE]. Assim o inteiro +1998 será copiado para o acumulador como o inteiro longo +00001998.
Ao copiarmos o conteúdo do acumulador no endereço EE (instrução CAE), [EE] recebe o sinal e os 4 dígitos menos significativos (à direita) de AC. Assim o inteiro longo +12345678 em AC será copiado para a posição de memória EE como o inteiro +5678.
O HIPO só guarda números inteiros na memória, o que faríamos para guardar números fracionários? Lembrem-se que um número no padrão HIPO, um inteiro, é um sinal seguido de quatro algarismos decimais, como +1234, -3141, ou generalizando, ± DDDD.
Agora, nada nos impede de imaginar um ponto no meio deste número, por exemplo assim: ± DD.DD Isso corresponderia a olharmos +1234 na memória como o número 12.34. Assim podemos representar números de -99.99 até +99.99 na memória do HIPO. Nada nos impede de imaginar este ponto em posições diferentes: ± DDD.D ou ± D.DDD , etc. Este método só é bom quando os números têm o mesmo "tamanho", ou seja, estão mais ou menos próximos. Porém, não poderíamos fazer a conta 1000 ´ 2.2 = 2200 usando a representação de ponto fixo no HIPO (pois o ponto precisaria estar num lugar diferente em cada número). Esta conta seria possível se tivéssemos um ponto móvel ("ponto flutuante" em gíria de computação).
Uma maneira (existem várias outras) de representar um número com ponto flutuante no HIPO é colocar em uma posição de memória a mantissa (os algarismos do número), e noutra colocar o expoente (a posição do ponto). Um número com ponto flutuante no formato HIPO será doravante denominado flutuante.
Para colocar um número no formato flutuante primeiro o escrevemos em notação científica:
230 = 2.30 ´ 102 = 2.3E+2.
Depois codificamos o número no formato: ± MMMM ± EEEE onde ± MMMM é a mantissa; no nosso exemplo: +2300; e ± EEEE é o expoente, no nosso exemplo: +0002.
Note que imaginamos um ponto fixo após o primeiro dígito da mantissa, dígito este sempre diferente de zero (± M.MMM). Outro exemplo: 0.00000032 = 3.2E-7. Aqui a mantissa é +3200 e o expoente é -0007.
Este truque de colocarmos um número em duas posições de memória é bastante comum (normalmente os números são colocados em várias posições de memória) e não é muito difícil programarmos o computador para fazer contas corretamente com flutuantes. No entanto, este método de representar números tem limitações : 12345 não pode ser representado exatamente, nem 1/3 = 0.333..., Ö 2 = 1.414213562... ou p = 3.14159265...
Uma operação aritimética (+,-,*,/) entre dois flutuantes é denominada um FLOP. O numero de FLOPs que um computador consegue realizar por segundo é uma das medidas mais importantes de seu desempenho. Para aumentar seu desempenho o computador tem muitas vezes uma unidade de processamento auxiliar, sob comando da CPU, especializada em FLOPs: é a FPU. Como curiosidade, compare a FLOPagem (FLOPs/segundo) de alguns computadores no mercado:
Computador |
CPU / FPU |
FLOPagem |
PC-XT |
Intel 8086 / Sem FPU |
1E3 |
PC-XT |
Intel 8086 / Intel 8087 |
50E3 |
PC-AT |
Intel i486 com FPU incorporada |
5E6 |
PC-AT |
Placa com coprocessador i860 |
50E6 |
CRAY-1 |
proprietária |
50E6 |
CRAY-XMP |
proprietária |
200E6 |
Touchstone Delta |
528 CPUs i860 |
10E9 |
Estas medidas de desempenho são freqüentemente dadas em *FLOPs, onde * indica a inicial de: Kilo=103, Mega=106, Giga=109 ou Tera=1012.
Considere os seguintes números: 19 e 91. Embora os dois números contenham os mesmos dígitos (1 e 9) sabemos que eles são diferentes e, além disso, que o segundo número é maior que o primeiro (pois eles têm o mesmo número de dígitos e o dígito mais à esquerda de 91 é maior que o dígito mais à esquerda de 19).
O sistema decimal é um sistema de numeração que usa a base de numeração 10, como o próprio nome indica. Cada número é escrito usando-se os dígitos 0, 1, 2, 3, 4, 5, 6, 7, 8 e 9 e a cada algarismo deste número corresponde um valor numérico que depende da posição desse algarismo no número. Assim, por exemplo, o número 1992 é uma notação abreviada do seguinte:
1992 = 1000 + 900 + 90 + 2 = 1*1000 + 9*100 + 9*10 + 2 = 1*103 + 9*102 + 9*101 + 2*100
Formalizando este conceito de base de numeração, em uma base qualquer B (no caso decimal, B = 10), um número N é representado explicitando-se a base por
N = (dm dm-1 ... d1 d0)B
onde dm, ..., d1 e d0 são seus dígitos, representados pelos algarismos 0 a B-1. O valor de N é dado por
N = dm*Bm + dm-1*Bm-1 + ... + d1*B1 + d0*B0
Os computadores são construídos utilizando-se, por causa de fatores tecnológicos, um sistema binário de representação interna de números e de caracteres. Assim, cada dígito de uma "palavra" pode conter apenas os algarismos 0 e 1. Neste sistema de numeração que usa apenas dois algarismos, estes são chamados de bit (do Inglês, "binary digit"). No entanto, a grande maioria dos computadores usa um agrupamento de 8 bits como unidade de informação, i.e. cada posição de memória contem 8 bits. Esses agrupamentos de 8 bits são chamados de Byte, e podem representar até 28=256 números ou caracteres.
Observação: No caso do HIPO, convencionamos utilizar o sistema decimal para manter a simplicidade do modelo.
Exemplo: (1101)2 é um número cujo valor é
(1101) = 1*23 + 1*22 + 0*21 + 1*20 = 8 + 4 + 0 + 1 = 13
Outras bases de numeração bastante utilizadas em computação são:
octal, ou base 8, que usa os dígitos 0, 1, 2, 3, 4, 5, 6, 7.
hexadecimal, ou base 16, que usa os dígitos 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, f onde os algarismos a ... f valem, respectivamente, 10 ... 15.
A idéia de definir um modelo didático de computador (como o HIPO) é bastante antiga e difundida. O exemplo mais famoso desta prática é o computador MIX apresentado em [Knuth69]. A presente apostila contém, com pequenas alterações, material de [Setzer84] e [Setzer91].
[Knuth69]. D.E.Knuth, The Art of Computer Programming Vol.1, Addison-Wesley Publishing Company, Readind.
[Setzer84]. W.W.Setzer e R.Terada, Introdução à Computação e à Construção de Algorítmos. IME-USP, São Paulo.
[Setzer90]. W.W.Setzer, Apostila do Dia da Computação. IME-USP, São Paulo.