UFU / FACOM / Autran Macêdo

Introdução à Ciência da Computação (INF64)

Programa de Computador

Afinal, por que um computador "funciona"?
Bem esta é uma pergunta cuja resposta envolve toda Ciência da Computação. Mas sendo sucinto, um computador (hardware) "funciona" porque um programa (software) está inserido nele. Esse programa implementa uma seqüência de instruções que deve ser seguida pelo computador para a realização de um objetivo.

Um programa que é fundamental ao computador é o sistema operacional (SO). Esse programa é o responsável por tornar "fácil" o uso do computador. Uma vez "carregado" no computador, é o SO que torna possível o uso de componentes como o teclado, o vídeo, a unidade de DVD, a porta USB, e também o uso de outros programas como editores de texto, planilhas e navegadores da Internet.

Em um computador que segue a arquitetura de von Neumann, qualquer programa, para ser executado, necessita estar armazenado na memória. Somente depois de carregado na memória é que o processador (que inclui a Unidade de Controle e a Unidade Lógica e Aritmética) consegue executar as instruções do programa na seqüência especificada.

Mas como se faz um programa?
Um programa é uma seqüência de instruções que deve ser seguida para se atingir um objetivo. Em geral, esse objetivo consiste da apresentação da solução para um problema. Por exemplo, "preciso de um programa que calcule a soma de dois números naturais quaisquer". Inicialmente devemos estudar e entender o problema (neste caso, o problema é bem simples, mas a maioria dos problemas interessantes são bastante complexos). Em seguida devemos escrever um algoritmo.

Similarmente a um programa, um algoritmo é uma seqüência de instruções que deve ser seguida para se atingir um objetivo. Só que o algoritmo é um "texto" para um ser humano seguir. (Uma receita de bolo é um algoritmo: o problema que esse algoritmo procura resolver é "como transformar os ingredientes do bolo em bolo - de preferência saboroso".) Um exemplo de algoritmo para o problema da soma de números, mencionado anteriormente, é apresentado abaixo:

1) Seja a um número Natural qualquer
2) Seja b um número Natural qualquer
3) Repita os passos 4 e 5, enquanto b>0
    4) a = a + 1
    5) b = b - 1
6) Mostre a

O algoritmo acima resolve o problema. Além disso, ele foi escrito de uma forma inteligível ao ser humano. Isto é, um ser humano é capaz de seguir e realizar os passos do algoritmo. Mas para esse algoritmo ser executado por um computador (de von Neumann), precisamos traduzi-lo para a linguagem binária. Além disso, é necessário que essa tradução esteja em conformidade com o conjunto de instruções do computador alvo.

Como traduzir um algoritmo em linguagem binária?
Nos primórdios da computação digital, a tradução de algoritmos para a linguagem binária era realizada manualmente. Isto é, o programador escrevia o algoritmo em termos de zeros e uns, conforme a organização (quantidade de registradores, por exemplo) e o conjunto de instruções da máquina alvo.

Vamos considerar um computador hipotético dos primórdios da computação digital e chamá-lo de CHipo0012. O CHipo0012 possui apenas 1 (um) registrador de dados (utilizado pela Unidade Lógica e Aritmética - ULA) e o conjunto de instruções constante na Tabela 1.

Código Instrução
0 Finaliza o Programa
1 Carrega <mem> no registrador de dados
2 Carrega <input> na memória
3 Incrementa de 1 o conteúdo do registrador de dados
4 Decrementa de 1 o conteúdo do registrador de dados
5 Apresenta <mem>
6 Salta para um endereço de memória
7 Compara se registrador de dados > 0, se sim salta <mem>
8 Carrega conteúdo registrador de dados na <mem>
Tabela 1: conjunto de instruções do CHipo0012.

As instruções apresentadas acima são aquelas que o Chipo conseguia executar (na época - década de 60). Portanto, um programa para essa máquina deveria consitir de uma seqüência adequada dessas instruções. Uma ilustração do CHipo0012 é apresentado na Figura 1.


Figura 1: ilustração do CHipo0012 (um computador hipotético dos primórdios da computação digital - década 60)

Os botões verdes representavam os oito bits de um byte. Quando se pressionava um dos botões, esse botão ficava aceso, indicando que o bit correspondente estava ligado. Esses botões eram utilizados para se inserir instruções e dados na memória do computador. A inserção se dava quando se pressionava o botão INPUT.

Quando todo o programa estava na memória, o botão RUN era pressionado. O programa então "tomava conta" do computador (naquela época os SOs eram ainda muito incipientes). O resultado da computação era apresentado pelos botões verdes (juntamente com o indicador OUTPUT aceso) ou quando muito numa impressora. Quando o programa terminava o indicador END ficava aceso. Finalmente, o botão CLEAR era pressionado quando se desejava "esvaziar" a memória.

O programador, com base no conjunto de instruções, tinha de condificar as instruções para o Chipo num determinado formato. O formato da instrução é similar ao apresentado na Figura 2.


Figura 2: formato da instrução do computador CHipo 0012.

Com esse formato, a cada 16 bits havia uma instrução na memória do computador. O código da instrução (a ser executada) deveria estar expressa nos 4 (quatro) bits mais significativos, os quatro bits seguintes não tinham uso, e os 8 (oito) bitas restantes eram utilizados para especificação de emndereço. Por exemplo, se tivéssemos que transferir o conteúdo do endereço 184 de memória para o registrador de dados, o programador deveria digitar no painel do CHipo a seguinte instrução

0001 0000 1011 1000

Observe que 0001 (dos 4 bits mais significativos) corresponde à representação binária do código da instrução 1 (vide Tabela 1), que realiza transferência do conteúdo de um endereço de memória para o registrador de dados. O endereço em questão está especificado nos 8 (oito) bits menos siginifcativos.

Vejamos um outro exemplo. Suponha que o número 3 está arqmzenado no endereço de memória 192. Quais instruções que o programador deveria inserir no computador para que esse endereço passasse a conter o número 2? As instruções poderiam ser as seguintes:

0001 0000 1100 0000    coloca o 3 (que está no endereço 192) no registrador
0100 0000 0000 0000    decrementa o conteúdo do registrador
1000 0000 1100 0000    coloca o conteúdo do registrador (que agora é 2) de volta ao endereço 192

Pois bem, para implementar o algoritmo de soma de números naturais apresentado inicialmente, um programador deveria escrever um programa similar ao da Tabela 2.

Tabela 2: programa em linguagem binária que implementa o algoritmo de soma de dois números naturais
End.Mem. Instrução Comentário
0 0010 0000 1100 0000 Carrega INPUT na mem.(208)
16 0010 0000 1101 0000 Carrega INPUT na mem.(224)
32 0001 0000 1101 0000 Carrega o conteúdo da mem.(224) no reg.
48 0110 0000 1010 0000 Salta para mem.(160)
64 0001 0000 1101 0000 Carrega o conteúdo da mem.(208) no reg.
80 0011 0000 0000 0000 Incrementa o conteúdo do reg.
96 1000 0000 1101 0000 Coloca o conteúdo do reg. na mem.(208)
112 0001 0000 1110 0000 Carrega o conteúdo da mem.(224) no reg.
128 0100 0000 0000 0000 Decrementa o conteúdo do reg.
144 1000 0000 1110 0000 Coloca o conteúdo do reg. na mem.(224)
160 0111 0000 01000 0000 Se o conteúdo do reg.>0, salta para mem.(64)
176 0101 0000 1101 0000 Mostra conteúdo da mem.(208)
192 0000 0000 0000 0000 Fim
208 ? Dado qualquer digitado pelo usuário
224 ? Dado qualquer digitado pelo usuário

Portanto, o programador dos primórdios tinha de conhecer a origanização da máquina e o seu conjunto de instruções. Além disso, também tinha de programar em binário, que por si só era uma tarefa suscetível a erro e de difícil depuração. E se você acha que o CHipo não existiu, veja a Figura 3. Trata-se de um computador da empresa HP modelo 2100, no qual o CHipo foi inspirado.


Figura 3: imagem do computador HP2100 retirada da página Computer History Museum Old "Visible Storage" de Ed Thelen.

Ainda hoje é assim que se transforma algoritmo em liguagem binária?
Não. Hoje em dia escolhemos uma linguagem de programação, codificamos o algoritmo nessa linguagem, e obtemos o código binário correspondente por meio de um outro programa, chamado compilador.

Uma linguagem de programação é uma linguagem projetada para especificar programas de computador. A primeira linguagem de programação foi a linguagem Assembly. Essa linguagem permitia que o programador utilizasse símbolos que representavam as instruções da máquina. Por exemplo, suponha que o programador tivesse que "carregar o conteúdo do endereço de memória 224 no registrador de dados". Ao invés do programador escrever na linguagem binária a instrução

0001 0000 1101 0000

ele poderia escrever

load 224

Um avanço formidável. Agora, o programador não tinha de se preocupar com a conversão correta para a linguagem binária. Essa conversão estava a cargo de um outro programa que converteria a expressão "load 224" na instrução binária correspondente.

Depois do Assembly, outras propostas de linguagem de programação surgiram, de modo que hoje existe uma plêiade de linguagens, para os mais variados tipos de problemas.

Finalmente, o compilador. O compilador de uma linguagem é um programa cujo objetivo é ler um programa escrito nessa linguagem e gerar a linguagem binária correspondente para a máquina alvo. Por exemplo, o gcc é o compilador da linguagem C para SOs baseados em GNU/Linux.