Published on

Arquitetura de computadores, o absoluto básico

Authors

Introdução

É fundamental que, antes de aprender a programar, se tenha um entendimento básico sobre como um computador interpretará seus programas. Este artigo objetiva cobrir o limitante inferior nesse aspceto.

A arquitetura dos computadores modernos é baseada em um modelo conhecido como arquitetura de Von Neumann, a qual divide o computador em duas partes principais:

  1. CPU (Central Processing Unit), ou unidade de processamento central em português.

  2. Memória.

Esta arquitetura é utilizada em todos os computadores modernos, sejam eles computadores pessoais, celulares, supercomputadores ou mainframes.

Memória

A memória dos computadores é composta por uma sequência enumerada de endereços de armazenamento com tamanho fixo. Isso quer dizer que caso você tenha 256mb de memória, por exemplo, existem cerca de 256 milhões de endereços de tamanho fixo na sua máquina. Cada endereço tem um nome e todos eles compartilham o mesmo tamanho. Em um enderço de memória do computador podemos armazenar uma única coisa: um número.

Note que embora um computador seja utilizado para uma série de tarefas distintas, tudo é acontece na memória. Não apenas os dados armazenados são armazenados na memória, mas também os programas habitam a memória. Dada a arquitetura de Von Neumann, não há diferença "real" entre programas e dados. Eles são, em última análise, objetos do mesmo tipo, e diferem exclusivamente pela forma como são utilizados pelo computador.

CPU

Surge então naturalmente a questão, como funciona um computador? Armazenar dados não é o bastante, precisamos ser capazes de também acessá-los, manipulá-los e movê-los.

Esse é o papel da CPU.

A CPU lê sequencialmente instruções da memória e as executa. Este processo é conhecido como Ciclo de Execução de Von Neumann, BUSCA-DECODIFICAÇÃO-EXECUÇÃO ou ainda em inglês fetch-execute-cycle. Para realizá-lo, alguns elementos são necessários:

  • Contador de programa

  • Decodificador de instruções

  • Barramento de dados

  • Registradores de propósito geral

  • Unidade lógica aritmética

O contador de programa é parte da unidade de controle do processador e é utilizado para dizer ao computador de onde obter as próximas instruções de. Lembre-se que foi anteriormente mencionado que não há diferença entre o armazenamento de dados e programas, eles apenas são interpretados de maneira distinta pela CPU. O contador de programa armazena o endereço de memória da próxima instrução a ser executada. A CPU inicia o ciclo pelo contador, obtendo qualquer que seja o número alocado no endereço de memória especificado. Este é então passado para o decodificador de instruções, o qual é responsável por interpretar a instrução passada. Isso inclui qual processo precisa ser executado como, por exemplo, adição, subtração, multiplicação, movimento de dados e também quais endereços de memória estarão envolvidos no processo. Instruções de computadores geralmente incluem ambos, instruções e endereços envolvidos no processo.

No próximo passo, o computador faz usso do barramento de dados para obter os endereços de memória que serão utilizados durante os cálculos. O barramento de dados é a conexção entre a CPU e a memória, consistindo literalmente nos fios que os conectam.

Além das memórias externas ao processador, o processador tem, ele mesmo, algumas memórias especiais de alta velocidade chamadas de registradores. Há dois tipos de registradores: registradores gerais e registradores de uso específico. Aqueles de uso geral são onde a maior parte da ação acontece. Adição, subtração, multiplicação, comparações e outras operações geralmente utiizam registradores gerais para o seu processamento. Entretanto, computadores possuem pouquíssimos registradores de propósito geral. A maior parte dos dados são armazenados na memória, transferidos para os registradores para serem processados e depois colocados de volta na memória quando o processamento é concluído. Registradores de uso específico são registradores tem propósitos extremamente particulares e serão tratados ulteriormente, quando forem relevantes.
Uma vez que a CPU tenha obtido todos os dados necessários, ela transfere os dados assim como a instrução decodificada para a unidade lógica aritmética para mais processamentos. Aqui é onde as instruções são de fato processadas. Depois que os resultados da computação tenham sido calculados, eles são colocados nos barramentos e enviados ao endereço apropriado na mememória ou em um registrador, tal qual especificado pela instrução em execução.

Note que esta é uma explicação grosseiramente simplificada. Processadores avançaram de forma considerável nos últimos anos e são hoje muito mais complexos. Embora as operações fundamentais continuem as mesmas, elas são complicadas pelo uso de hierarquias de cache, processadores superescalares, pipelining, previsão ded ramo, execução fora de ordem, tradução de microcódigo, cooprocessadores e outros tipos de otimização.

Terminologia

Como já mencionado a memória do computador é uma sequência enumerada de localizações de armazenamento de tamanho fixo. O número associado a cada localização é chamado de endereço. O tamanho de um único local de armazenamento é chamado byte. Em processadores x86, um byte é simplesmente um número entre 0 e 255.

Um questionamento natural é como computadores são capazes de exibir texto, gráficos e até mesmo números enormes quando tudo que são capazes de fazer é armazenar núemros entre 0 e 255. Placas de vídeo, assim como outros tipos de harware especializados, possuem interpretações especiais para cada número. Ao exibirem texto na tela, computadores utilizam códigos da tabela ASII para traduzir os números que estamos enviando para letras na tela, com cada número sendo traduzido para exetamente uma letra ou numeral. Por exemplo, o caractere A é representado pelo número 65. O numeral 1 é representado pelo número 49 e assim por diante. Isso quer dizer que para exibir "Olá", você deveria passar a sequência 79, 108, 97, alt+160, alt aqui indica que o código correspondente a alt deve ser passado junto com o número 160 por conta do acento na letra a.

Além disso, como programador, podemos manipular os números. Se, por exemplo, estamos implementando uma loja, podemos utilizar um número para representar cada item à venda. Cada número tabmém pode ser ligado em uma série de outros números que também são códigos ASCII para aquilo que eu quiser exibir quando os items forem lidos. Poderíamos ter mais números para o preço, inventário e assim por diante.

Entretanto, isso leva à próxima pergunta: E números maiores que 255? Podemos simplesmente utilizar combinações de bytes para representar números maiores. Dois bytes podem ser utilizados para representar qualquer número entre 0 e 65536. Quatro bytes podem ser utilizados para representar qualquer número entre 0 e 4294967295. Note entretanto, que é um pouco complicado ficar juntando bytes em programas para aumentar o tamanho dos nossos números e também requer um pouco de matemática. Felizmente, o computador fará isso em nosso lugar para números de até 4 bytes. Na verdade, números ded 4bytes são o padrão.

Anteriormente mencionamos também que, além da memória usual, temos os registradores. Registradores são o que o computador utiliza no processo de computação. Uma boa ananalogia para registradores é a mesa. Sua mesa de trabalho segura as coisas sobre as quais você está trabalhando. Você tem muita informação espalhada entre gavetas e armários mas aquilo com que você de fato está trabalhando fica em cima da mesa. Os registradores guardam o conteúdo dos números que você está manipulando naquele momento.

nos computadores que estamos utilizando, registradores tem 4 bytes cada. O tamanho de um registrador típico é dito o tamanho da palavra do processador. Processadores x86 possuem palavras de 4 bytes. Isso quer dizer que as computações mais naturais para esses computadores manipulam 4bytes por vez, o que gera, em média 4 bilhões de valores.

Endereços também tem 4 bytes (1 palavra) de comprimento, e portanto, cabem em um registrador. Perceba que isso faz com que possamos armazenar endereços da mesma forma com que armazenamos qualquer outro número. Na verdade, o computador não é capaz de diferenciar se o valor é um endereço, um número ou um código ASCII. Um número se torna código ASCII quando você tenta exibí-lo e um endereço quando você tenta acessar o byte para o qual aponta. Isso é crucial para o entendimento do funcionamento de programas.

Endereços que estão guardados na memória também são chamados de ponteiros já que, ao invés de armazenarem um valor ordinário, apontam para um local diferente da memória. Como já dito, as instruções para o computador também estão guardadas na memória, não só isso, mas estão armazenadas exatamente como qualquer outro dado. A única forma que o computador tem de saber que uma localização da memória é uma instrução é se um registrador de uso específico chamado de instrutor de ponteiro (instruction pointer), aponta eles a um ponto ou outro. Caso o instrutor de ponteiro aponte para a palavra chave memória, o dado é carregado como uma instrução. Caso contrário, o computador não tem como diferenciar programas e outros tipos de dados.

Modos de endereçamento de dados

Processadores tem vŕias maneiras diferentes de acessar dados, as quais são conhecidas como modos de acesso. O modo mais simples é conhecido como modo imediato onde os dados a serem acessados estão contidos na própria instrução. Por exemplo, se queremos inicializar o registrador para 0, ao invés de passar ao computador um endereço para ler 0 de, podemos especificar o modo imediato e atribuir o número 0.

No modo de endereçamento a registro, a instrução contém um registrador a ser acessado, ao invés de um local ded memória. Os demais modos lidarão com endereços.

No modo de endereçamento direto, a instrução contém o endereço de memória para o acesso. Por exemplo, poderíamos passar como par0metros os dados e o registrador, suponhamos 2002. O processador iria então diretamente ao byte número 2002 e copiaria seus conteúdos para nosso registrador.

No modo de endereçamento indexado a instrução contém o endereço da memória a ser acecssada e também específica um índice de registrador para deslocar aquele endereço. Por exemplo, poderíamos específicar o endereço 2002 e um índice. Se o registrador do índice contém o número 4, o endereço de onde os dados de fato seriam carregados seria 2006, Dessa forma, se você tem um conjunto de números começando em 2002, você pode percorrê-los através de um índice de registrador.

Em processadores x86 você também pode especificar um multiplicador como o índice. Isso permite que você acesse a memória um byte por vez, ou uma palavra por vez (4bytes). Se você está acessando uma plavra inteira, seu índice deverá ser multiplicado por 4 para retornar a localização exata do quarto elemento do seu endereço.

Por exemplo, se você quiser acessar do quarto byte do local 2002, você precisaria carregar o seu índice de registrador com 3, já que a contagem começa do 0, e definir seu multiplicador como 1, já que está percorrendo 1 byte por vez. Isso retornaria o local 2005. Entretanto, se você quisesse acessar a quarta palavra a partir da localização 2002, você precisaria carregar seu índice de registrador com 3 e definir o multiplicador como 4. Isso carregará do local 2014, a quarta palavra.

No modo de edereçamento indireto a instrução contém um registrador que contém um ponteiro para onde o dado deve ser acessado. Por exemplo, se utilizássemos o modo indireto e especificássemos o registrador %eax e ele contivesse o valor 4, qualquer que fosse o valor no endereço 4 de memória seria utilizado. Em endereçamento direto, simplesmente carregariámos o valor quatro, mas em endereçamento indireto, utilizamos 4 como o endereço para encontrarmos o dado que estamos procurando.

Por fim, há o endereçamento de base. Ele é similar ao modo indireto, mas você também inclui um número chamado deslocamento(offset) para adicionar ao valor do registrador antes de utilizá-lo para busca.

Há outras formas de endereçamento, mas essas são as mais preponderantes.

Conclusão

Estes são os conceitos básicos para se compreender como linguagens como Assembly, C e C++ funcionam. Naturalmente cobrem uma pequeníssima área daquilo que é conhecido como arquitetura de computadores, mas consitutem o pontapé inicial para aventuras em linguagens com acesso a controle de baixo nível

Referências

Referências:

  1. Bartlett, JonathanProgramming from the ground up.Bartlett Publishing 2004.
  2. Irvine, Kip Assembly language for x86 processors Prenitence Hall 2010.