Arquitectura de Computadores

Notas de estudo

Alberto José Proença

1999/00

 

Índice geral

 

  1. Organização e arquitectura dum computador
  2. Análise do funcionamento do CPU dum computador
  3. Mecanismos para execução de programas
  4. Modelo de programação dum processador
  1. Conjunto de instruções presente num processador RISC
  2. Registos visíveis ao programador
  3. Convenção na utilização dos registos do MIPS
  4. Modos de acesso aos operandos
  5. Instruções de input/output
  6. Ordenação de bytes numa palavra

 

Nos capítulos anteriores fez-se a análise do funcionamento dum computador (e em particular o binómio processador-memória), e o estudo dos mecanismos que permitem passar de um programa escrito por um utilizador e armazenado em disco, para o programa codificado e armazenado na memória principal do computador, pronto a ser executado. O tema deste capítulo é a descrição do modelo de programação que cada processador deve apresentar ao programador em linguagem máquina ou assembly -incluindo a identificação do tipo de instruções que se deve esperar encontrar num instruction set - e alguns detalhes da especificação de operandos (com análise dum processador em particular, o MIPS). Este material encontra-se parcialmente descrito no anexo A do texto de apoio (em A.6, A.8, e A.10).

 

  1. Conjunto de instruções presente num processador RISC

O conjunto de instruções que cada processador suporta é bastante variado. Contudo é possível identificar e caracterizar grupos de instruções que se encontram presentes em qualquer arquitectura:

Uma primeira apresentação global do instruction set do MIPS permite verificar como este processador cobre com um mínimo de detalhe estes grandes grupos. A secção A.10 apresenta detalhadamente todas as instruções do MIPS, bem como algumas pseudo-instruções. A tabela que se segue é uma compilação resumida do instruction set do MIPS R2000.

Grupo Sintaxe

Tipo

Comentário
  lb Rdest, Imm16(Rsrc)

I

load byte
  lw Rdest, Imm16(Rsrc)

I

load word
  lbu Rdest, Imm16(Rsrc)

I

load unsigned byte
  sb Rsrc2, Imm16(Rsrc1)

I

store byte

Transferência

sw Rsrc2, Imm16(Rsrc1)

I

store word

de

lui Rdest, Imm16

I

load upper immediate

Informação

mfhi Rdest

R

move from hi
  mflo Rdest

R

move from low
  mthi Rsrc

R

move to hi
  mtlo Rsrc

R

move to low
  la Rdest, address

-

load address
______________ move Rdest, Rsrc

-

move
  add Rdest, Rsrc1, Rsrc2

R

addition
  addi Rdest, Rsrc1, Imm16

I

addition immediate
  sub Rdest, Rsrc1, Rsrc2

R

subtract
  mult Rsrc1, Rsrc2

R

multiply

Operações

multu Rsrc1, Rsrc2

R

unsigned multiply

aritméticas

div Rsrc1, Rsrc2

R

divide
  divu Rsrc1, Rsrc2

R

unsigned divide
  abs Rdest, Rsrc

-

absolutevalue
  mul Rdest, Rsrc1, Rsrc2

-

multiply
  div Rdest, Rsrc1, Rsrc2

-

divide
______________ rem Rdest, Rsrc1, Rsrc2

-

remainder
  and Rdest, Rsrc1, Rsrc2

R

AND
  andi Rdest, Rsrc1, Imm16

I

AND immediate

Operações

or Rdest, Rsrc1, Rsrc2

R

OR

lógicas

ori Rdest, Rsrc1, Imm16

I

OR immediate

e de

xor Rdest, Rsrc1, Rsrc2

R

XOR

comparação

xori Rdest, Rsrc1, Imm16

I

XOR immediate
  nor Rdest, Rsrc1, Rsrc2

R

NOR
  slt Rdest, Rsrc1, Rsrc2

R

set less than
______________ slti Rdest, Rsrc1, Imm16

I

set less than immediate
  j address28

J

jump (absolute addr)
  jr Rsrc

R

jump register
  beq Rsrc1, Rsrc2, address18

I

branch on equal (relative addr)

Instruções

bne Rsrc1, Rsrc2, address18

I

branch on not equal (relat addr)

de

jal address28

J

jump and link (absolute addr)

salto

jalr Rsrc

R

jump and link register
  b address18/32

-

branch inconditional (relat addr)
  b<cnd> Rsrc1, Rsrc2, addr18/32

-

br on <cnd> = [gt, ge, lt, le] (relat addr)
  b<cnd>u Rsrc1, Rsrc2, addr18/32

-

br on <cnd> = [gt, ge, lt, le] uns (r.addr)
  1. Registos visíveis ao programador

Um dos aspectos essenciais para um programador em assembly é saber de quantos registos dispõe, qual a sua dimensão, e em que circunstâncias esses registos podem ser usados.

• Em arquitecturas RISC

No caso das arquitecturas RISC, a grande maioria dos processadores possui 32 registos genéricos de 32 bits - para representar valores inteiros e/ou endereços de memória - para além de 32 registos de 32 bits para representação de valores em vírgula flutuante. Estes registos são considerados de uso genérico e podem ser usados explicitamente por qualquer instrução que aceda a operandos em registos, com excepção de um registo que contém o valor zero e que não pode ser alterado (só de leitura). Ainda visível ao programador está sempre também o apontador para a próxima instrução, o instruction pointer ou program counter.

• Na arquitectura MIPS

A arquitectura MIPS contém também 32 registos genéricos de 32 bits (0 ao 31), para além do pc, sendo o registo 0 apenas para leitura com o valor zero; dos restantes, apenas o registo 31 é implicitamente usado por uma instrução (de invocação de uma rotina, para guardar o endereço de retorno). Adicionalmente, o MIPS contém ainda 2 registos para poder operar com operandos de 64 bits, como acontece no caso da multiplicação e da divisão, chamando-lhes de hi(gh) e lo(w). A arquitectura MIPS prevê ainda 32 registos de 32 bits para vírgula flutuante (f0 a f31), os quais poderão ser organizados em (até) 16 registos de precisão dupla, com 64 bits (as designações par dos registos). As primeiras implementações da arquitectura MIPS (família R20x0 e R30x0) não dispõem de unidade de vírgula flutuante incluída no microprocessador, implementando estas funções em coprocessador separado (R2010 e R3010), de acordo com o que foi dito anteriormente.

• Em arquitecturas CISC (M680x0, ix86)

A existência de um grande n.º de registos nas arquitecturas RISC, aliado à evolução da tecnologia dos compiladores dos últimos anos (em especial na geração de código), vem permitindo representar a maioria das variáveis escalares directamente em registo, não havendo necessidade de recorrer com tanta frequência à memória. Esta organização não foi contudo economicamente viável nas gerações anteriores de microprocessadores, com destaque para a família da Motorola (M680x0) e, ainda mais antiga, a família da Intel (ix86). Estes processadores dispunham de um menor n.º de registos e, consequentemente, uma diferente organização que suportasse eficientemente diversos mecanismos de acesso à memória.

No caso da família M680x0, o programador tinha disponível dois bancos de 8 registos genéricos de 32 bits: um para dados (D) e outro para apontadores para a memória (A), suportando este último banco um variado leque de modos de endereçamento à memória. Apenas um dos registos (A7) é usado implicitamente em certas operações de manuseamento da stack..

A família da Intel é mais complicada, por não ter verdadeiramente registos de uso genérico. A arquitectura de base dispõe efectivamente de 4 registos para conter operandos aritméticos (A, B, C e D), mais 4 para trabalhar com apontadores para a memória (BP, SP, DI e SI) e outros 4 para lidar com uma memória segmentada (CS, DS, SS e ES; a única maneira de uma arquitectura de 16 bits poder aceder a mais de 64k células de memória). Cada um destes registos não pode ser considerado de uso genérico, pois quase todos eles são usados implicitamente (isto é, sem o programador explicitar o seu uso) em várias instruções (por ex., os registos A e D funcionam de acumuladores em operações de multiplicação e divisão, enquanto o registo C é usado implicitamente como variável de contagem em instruções de controlo de ciclos). A situação complica-se ainda mais com a variação da dimensão dos registos na mesma família (de registos de 16 bits no i286 para registos de 32 bits no i386), pois o formato de instrução foi concebido para distinguir apenas operandos de 8 e de 16 bits, e um bit bastava; para garantir compatibilidade ao longo de toda a arquitectura, os novos processadores têm de distinguir operandos de 8, 16 e 32 bits, usando o mesmo formato de instrução!

  1. Convenção na utilização dos registos do MIPS

A arquitectura do MIPS disponibiliza 32 registos genéricos para ser usado pelo programador como este muito bem entender. Contudo, se se convencionar um conjunto de regras na utilização desses registos, poder-se-á conseguir uma maior portabilidade de programas objecto/executáveis entre sistemas de computação que usem a mesma família de processadores (MIPS neste caso). Esta situação é mais crítica no caso do código gerado pelos compiladores e assemblers.

Para que se tenha uma maior sensibilidade às consequências da adopção dessa convenção na utilização dos registos do MIPS, ela vai ser parcialmente exposta aqui e usada no futuro sempre que se usarem exemplos e/ou exercícios com programas em assembly do MIPS.

Para já é relevante que se observem as seguintes regras na utilização desses registos:

  1. Modos de acesso aos operandos

Os operandos em assembly estão armazenados algures dentro do computador: ou em registos, ou na memória. A escolha de um destes modos de acesso vem indicada explicitamente no formato de instrução da operação que se pretende que o CPU efectue.

Nas arquitecturas RISC as operações aritméticas/lógicas impõem que os operandos estejam em registos no CPU: ou em registos explicitados pelo programador (um dos 32 registos genéricos, ou ainda de fp), ou no registo de instrução (fazendo parte do formato de instrução, também designado por modo de acesso imediato).

O acesso à memória nas arquitecturas RISC faz-se normalmente através de operações de load ou de store. Nestas operações é possível nalgumas arquitecturas especificar mais de uma maneira de calcular o endereço da posição de memória que se pretende aceder; esta riqueza de modos de endereçamento é uma das características das arquitecturas CISC. No caso do MIPS é apenas possível especificar um endereço de memória (para uma instrução de load ou store) duma única maneira: um valor numérico de 16 bits e um registo; a posição de memória que se acede vem dada pela soma desse valor com o conteúdo do registo especificado. Quando se pretender aceder a um operando que se encontra numa posição de memória, cujo endereço de mais de 16 bits se dispõe, a metodologia a seguir é a seguinte: carregar primeiro os 16 bits mais significativos do registo a usar como apontador (usando a instrução de lui, load upper immediate, a qual deixa a zero os 16 bits menos significativos), e usar depois o load normal com o modo de endereçamento especificado anteriormente.

  1. Instruções de input/output

O acesso aos periféricos dum computador é feito normalmente pelos seus controladores, ligados aos barramentos do computador. Estes controladores desempenham quase todas as tarefas indispensáveis ao bom funcionamento dos periféricos; apenas necessitam de ser devidamente configurados inicialmente, posteriormente activados com comandos específicos, e estabelecer uma comunicação com a informação a transferir de/para o computador. Assim, o CPU apenas precisa de aceder a esses controladores para 3 tipos de acções:

Para o desempenho destas actividades, a maioria dos processadores não dispõe de instruções específicas de input/output; basta apenas usar as instruções normais de acesso à memória para efectuar a leitura ou escrita dos registos dos controladores. O descodificador de endereços do computador se encarregará de gerar os sinais apropriados de chip select para seleccionar o controlador necessário, de acordo com o mapa de alocação da memória que o projectista do computador definiu. A este modelo de mapeamento do espaço endereçável de input/output no espaço endereçável de memória é designado por memory mapped I/O. A secção A.8 mostra um exemplo no simulador do MIPS.

Outros processadores, como os da família ix86, dispõem adicionalmente de instruções específicas de I/O, que implementam essencialmente a operação de ler um registo dum controlador (também chamado de uma porta) - in - ou a escrever numa porta - out..

  1. Ordenação de bytes numa palavra

A dimensão de cada célula de memória (8 bits) não é suficientemente grande para armazenar integralmente o conteúdo dum registo (32 bits). Quando se pretende armazenar o conteúdo dum registo na memória, a partir de um dado endereço, duas alternativas básicas se colocam em relação à ordem em que os blocos de 8 bits da palavra são colocados na memória:

A arquitectura do MIPS pode suportar ambas, mas apenas uma de cada vez que é feito o reset do CPU.