Buscar

Introdução à Arquitetura de Computadores

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 24 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 24 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 24 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

PDF gerado usando o pacote de ferramentas em código aberto mwlib. Veja http://code.pediapress.com/ para mais informações.
PDF generated at: Fri, 06 Dec 2013 10:33:27 UTC
Introdução à Arquitetura de
Computadores
Conteúdo
Páginas
Capa 1
Introdução 2
O que é o MIPS? 4
Instruções do MIPS 5
Representação das Instruções 9
As Pseudo-Instruções 10
Suporte à Funções 11
Representação Numérica 14
As Operações da Unidade Lógica Aritmética 17
Referências
Fontes e Editores da Página 20
Fontes, Licenças e Editores da Imagem 21
Licenças das páginas
Licença 22
Capa 1
Capa
Introdução à Arquitetura de Computadores
Clique aqui para ir ao índice
Ir para o índice
A imagem de capa deste wikilivro é de uma réplica do Cray-1 - o primeiro supercomputador da história. A réplica
está em um museu alemão. O Cray-1 foi construído em 1971 e era mais veloz qque qualquer outro computador da
época. De fato, demorou décadas para que surgissem computadores capazes de superar o Cray-1 em velocidade. Ele
era capaz de efetuar 160 milhões operações aritméticas por segundo e custava U$700.000,00.
O desenvolvimento de uma máquina tão rápida só foi possível graças à pesquisas em Arquitetura de Computadores.
A Arquitetura de Computadores é ciência de selecionar e interconectar componentes de hardware para produzir
computadores que atendam a requisitos funcionais, de desempenho e custo. Ela também pesquisa formas de tornar os
computadores máquinas mais eficientes, baratas e/ou rápidas.
Clique aqui para ir ao índice
Ir para o índice
Introdução 2
Introdução
O Que é Arquitetura de Computadores
A Arquitetura de Computadores é o projeto conceitual e fundamental da estrutura operacional de um sistema
computacional. Ela é o estudo dos requisitos necessários para que um computador funcione e de como organizar os
diversos componentes para obter melhores desempenhos.
Como computador entendemos qualquer tipo de dispositivo capaz de receber uma entrada e que retorna uma saída
após realizar uma série de operações com base nos valores recebidos e armazenados. Existem vários tipos de
computadores. Uma das formas de classificá-los é por meio das seguintes categorias:
• Desktop: Computadores de baixo-custo e com desempenho razoável para um usuário "comum".
• Servidor: Máquinas projetadas para ter um desempenho considerado bom para uma aplicação muito grande e
complexa ou então para um número muito grande de operações mais simples. Alguns servidores são simples
computadores de Desktop melhorados. Entretanto, existem também aqueles que possuem arquiteturas muito mais
sofisticadas que contam com dezenas ou até mesmo centenas de processadores.
• Sistemas Embarcados: Possuem um uso dedicado à uma única tarefa e normalmente vem embutidos em outros
aparelhos como celulares, microondas, elevadores ou veículos. Possuem uma Entrada/Saída muito simples.
Os princípios estudados em Arquitetura de Computadores são fundamentais para se projetar máquinas realmente
eficientes.
Computadores e as Várias Camadas de Abstração
Computadores são aparelhos extremamente complexos. Para compreender o seu funcionamento, precisamos
entender várias camadas de abstração diferente.
A camada mais baixa de todas é aquela formada por transistores, tensão e corrente elétrica. Quem costuma lidar com
esta camada são físicos e engenheiros elétricos. Nesta camada estuda-se o funcionamento de transistores e circuitos
sempre levando em conta as propriedades físicas da corrente elétrica. Abaixo vemos um desenho representando um
transistor.
Uma camada acima, estão as portas lógicas - todas elas compostas por transistores. Neste nível estuda-se como criar
estruturas mais complexas combinando-se as diversas portas como AND, OR e NOT para criar estruturas como
multiplexadores, flip-flops e somadores. Neste estágio poode-se usar lingüagens como o Verilog ou VHDL para
programar circuitos. Abaixo vemos desenhos que representam várias portas lógicas:
Introdução 3
Subindo mais um nível de abstração, começamos a lidar com estruturas mais complexas como registradores e
unidades lógicas aritméticas - todas compostas por muitos flip-flops, somadores e multiplexadores. Vemos como
todas essas estruturas realmente geram as instruções de cada máquina e como cada instrução funciona. É neste nível
que costuma trabalhar um Arquiteto. Este será o nível que será abordado ao longo deste Wiki-livro. Abaixo
mostramos a imagem de uma Unidade Lógica Aritmética - estrutura usada por computadores para realizar cálculos:
Um nível além, estuda-se como combinar as instruções da camada anterior para realizar comandos mais sofisticados
como as operações da lingüagem C e como coordenar o funcionamento de um sistema operacional por meio de
interrupções e outros recursos. A imagem abaixo é um diagrama que representa o Kernel de um Sistema Operacional
sendo usado como um meio de comunicação entre o Software e o Hardware:
Introdução 4
Acima desta camada, está o estudo do funcionamento de funções de bibliotecas, APIs e a programação de aplicativos
e programas de computador simples.E finalmente, na camada de abstração mais superior está o funcionamento de
um programa de computador do ponto de vista do usuário. Como utilizar um aplicativo já criado.
O que é o MIPS?
O Processador MIPS
O MIPS é o nome de uma arquitetura de processadores baseados no uso de registradores. As suas instruções tem à
disposição um conjunto de 32 registradores para realizar as operações. Entretanto, alguns destes registradores não
podem ser usados por programadores, pois são usados pela própria máquina para armazenar informações úteis.
O que é o MIPS? 5
Processadores MIPS são do tipo RISC (Reduced Instruction Set Computer - ou seja, Computadores com Conjunto de
Instruções Reduzidas). Isso significa que existe um conjunto bastante pequeno de instruções que o processador sabe
fazer. Combinando este pequeno número, podemos criar todas as demais operações.
Pela sua elegância e simplicidade, processadores MIPS são bastante usados em cursos de arquiteturas de muitas
universidades. Ele é considerado um processador bastante didático.
Projetos MIPS são atualmente bastante usados em muitos sistemas embarcados como dispositivos Windows CE,
roteadores Cisco e video-games como o Nintendo 64, Playstation, Playstation 2 e Playstation Portable.
A Filosofia do Projeto do MIPS
Quando o MIPS estava sendo desenvolvido, quatro regras foram definidas para guiar o projeto. Elas são a filosofia
do MIPS:
•• A simplicidade favorece a regularidade.
• O menor é (quase sempre) mais rápido. Levando em conta que uma corrente elétrica se propaga cerca de um
palmo por nanosegundo, circuitos simples são menores e portanto, mais rápidos.
•• Um bom projeto demanda compromissos.
• O caso comum DEVE ser mais rápido. É muito melhor tornar uma instrução que é usada 90% do tempo 10%
mais rápida do que fazer com que uma instrução usada em 10% das vezes torne-se 90% mais rápida. Esta regra é
baseada na "Lei de Amdahl".
Foram estas as regras usadas para decidir vários aspectos do funcionamento do MIPS - incluindo quais seriam suas
instruções e como elas funcionariam.
Instruções do MIPS
Vamos começar agora a estudar as instruções que existem em um computador MIPS. Para praticar e testar as
instruções caso você não tenha uma máquina MIPS é usar um simulador como o SPIM. O download do simulador
SPIM para Linux, Mac OS, Unix e Windows pode ser feito no link: [1].
O objetivo deste capítulo não é fornecer um guia de referência exaustivo sobre todas as instruções dos MIPS. Iremos
apenas apresentar um sub-conjunto mínimo de todas as instruções do MIPS que são o mínimo necessário para termos
um computador funcionando.
Instruções Aritméticas Simples
add $r1, $r2, $r3 # Esta instrução soma o conteúdo dos registradores
 # $r2 e $r3 colocando o conteúdo no registrador $r1
addi$r4, $r1, 9 # Agora estamos somando o conteúdo do registrador
 # $r1 com o valor imediato 9 e armazenando o
 # resultado em $r4. O número imediato deve ter
 # 16 bits.
addu $r5, $r6, $r4 # Quase igual ao add. Mas agora assumimos que
 # todos os valores são não-negativos.
addiu $r7, $r8, 10 # Somamos o conteúdo de $r8 com o valor imediato
 # 10 e armazenamos o resultado em $r7. Assume-se
Instruções do MIPS 6
 # que todos os valores são não-negativos. 
sub $r1, $r2, $r3 # Subtrai-se o conteúdo de $r3 do conteúdo de $r2
 # e coloca-se em $r1. Também existe subi, subu e
 # subiu que tem comportamento semelhante a addi,
 # addu e addiu, mas para a subtração.
Instruções de Operadores Lógicos
and $r1, $r2, $r3 # Realiza uma operação AND bit-a-bit entre $r3 e $r2.
 # O resultado é armazenado em $r1.
andi $r1, $r2, 42 # Realiza uma operação AND bit-a-bit entre $r2 e o valor
 # imediato 42. O resultado é armazenado em $r1. O número
 # imediato deve caber em 16 bits.
or $r1, $r2, $r3 # Realiza uma operação OR bit-a-bit entre $r3 e $r2.
 # O resultado é armazenado em $r1.
ori $r1, $r2, 42 # Realiza uma operação OR bit-a-bit entre $r2 e o valor
 # imediato 42. O resultado é armazenado em $r1. O número
 # imediato deve caber em 16 bits.
Podemos notar que as instruções seguem uma lógica clara. Até agora todas seguem uma mesma lógica. Além destas,
não existem instruções mais complexas como potências ou raízes quadradas.Somente as operações matemáticas mais
simples são representadas. Podemos ver aquelas 4 regras que formam a filosofia do projeto do MIPS em ação.
Instruções de Uso de memória
As instruções do uso da memória seguem uma lógica diferente das instruções de operações aritméticas. Pra começar,
existem três tipos de instruções capazes de copiar dados da memória para os registradores.
lw $r1, 4($r2) # Load Word: Esta instrução carrega uma palavra (estrutura de 4 bytes)
 # localizada no endereço representado pela soma do valor
 # armazenado no registrador $r2 mais 4. O resultado é armazenado em $r1.
lh $r1, 6($r3) # Load Half: Esta instrução carrega uma estrutura de 2 bits localizada
 # no endereço representado pela soma do valor armazeado no
 # registrador $r3 mais o número 6. O resultado é armazenado em $r1.
lb $r1, 16($r2)# Load Byte: Esta instrução carrega um byte (8 bits) localizado no
 # endereço representado pela soma do valor armazenado em $r2 mais o
 # número 16. O resultado é armazenado em $r1.
Perceba que desta forma é rápido de carregar o conteúdo de um valor em um vetor. Basta saber o endereço do valor
inicial do vetor e o índice de sua posição. Por exemplo, suponha que eu possua um vetor de caracteres char vetor[5].
Supondo que o registrador $r1 contenha o endereço de vetor[0], para armazenar o valor de vetor[3] no registrador
$r2 e assumindo que cada caractere possui um byte, basta usar a seguinte instrução:
lb $r2 24($r1)
Instruções do MIPS 7
Colocamos o 24 lá porque cada caractere ocupa 8 bits e queremos pegar o quarto caractere da seqüência. Logo,
precisamos nos deslocar 24 bits para acharmos o caractere certo. Afinal, a distância entre o primeiro elemento do
vetor armazenado em $r1 e o quarto item do vetor é de 24:
[0][ ][ ][ ][ ][ ][ ][ ].[1][ ][ ][ ][ ][ ][ ][ ].[2][ ][ ][ ][ ][ ][ ][ ].[3][ ][ ][ ][ ][ ][ ][ ]
Para armazenarmos conteúdo de um registrador na memória também existem 3 comandos:
sw $r1, 4($r2) # Store Word: Esta instrução carrega uma palavra (estrutura de 4 bytes)
 # localizada no registrador $r1 e armazena no endereço representado 
 # pela soma do valor armazenado no registrador $r2 mais 4. 
sh $r1, 4($r2) # Store Half: Esta instrução carrega uma estrutura de 2 bits
 # localizada no registrador $r1 e armazena no endereço representado 
 # pela soma do valor armazenado no registrador $r2 mais 4. 
sb $r1, 4($r2) # Store Byte: Esta instrução carrega um byte (8 bits)
 # localizado no registrador $r1 e armazena no endereço representado 
 # pela soma do valor armazenado no registrador $r2 mais 4. 
Vamos ver um exemplo de conversão de um código escrito em C para a lingüagem Assembly do MIPS assumindo
que um int da lingüagem C tenha 4 bits:
/* CÓDIGO C */
typedef struct A{
 int x;
 int y;
 int z;
 int w;
}aType;
aType V[16];
(...)
aPtr = &(V[3]);
m = aPtr -> y;
n = aPtr -> w;
aPtr -> x = m + n;
Vamos assumir que o vetor V esteja no endereço 0x0800.0000. A estrutura aType é formada por 4 valores numéricos
consecutivos, cada um com 4 bits. Logo, cada aType ocupa 16 bits (4*4). Ou, em hexadecimal, 0x010 bits. Veja uma
representação de um aType como é armazenado na memória:
[x][x][x][x][y][y][y][y][z][z][z][z][w][w][w][w]
Temos um vetor com 16 estruturas como esta chamado V. Logo, o vetor ocupa um espaço de 256 bits. Com estas
informações, concluímos que o código em Assembly necessário para fazer a operação mostrada no código em C
mostrado acima é:
Instruções do MIPS 8
la $r1, 0x0800.0030 # A instrução Load Address carrega em $r1 o endereço de V[3]. Falaremos sobre o la depois.
lw $r2, 4($r1) # Carregamos para $r2 o quarto bit à partir de V[3]. Ou seja, o valor de y.
lw $r3, 12($r1) # Agora carregamos para $r3 o valor do inteiro w.
add $r4, $r2, $r3 # Somamos os valores de y e w colocando o valor em $r4.
sw $r4, 0($r1) # Colocamos o valor da soma no lugar do x.
Instruções de Controle de Fluxo
Agora veremos instruções capazes de controlar o fluxo de execução de instruções de um computador. A primeira
delas é a instrução beq, ou Branch if Equal:
beq $r1, $r2, DESTINO
O que esta instrução faz é verificar se o valor de $r1 é igual à $r2. Caso isso seja verdadeiro, ela muda o valor do
registrador PC (Program Counter) que guarda o endereço da próxima instrução a ser executada. Se o valor passado
como destino for 0, nada acontece. Nenhuma instrução é pulada, o programa executa a próxima instrução
normalmente, independente de $r1 == $r2 ser verdadeiro ou falso. Se o valor passado for "1", pula-se uma instrução
se $r1 for igual a $r2. Se o valor passado for "2", pulam-se duas instruções e assim por diante. Também pode-se
passar valores negativos. Um valor de "-1" faz com que o programa entre em um loop infinito, nunca mais passando
para a próxima instrução. Uma "-2" faz com que voltemos uma instrução anterior, e assim por diante.
Entretanto, o valor de DESTINO só pode ser no máximo um valor com 16 bits. Com um valor destes, só podemos
pular no máximo cerca de 32 000 instruções para frente ou para trás. Isso não é um problema na maioria das vezes é
muito raro existir um programa que exija que você pule uma quantidade tão grande de instruções. Assumindo que 4
linhas de código e Assembly correspondem a 1 linha em código C, para que pulemos uma quantidade tão grande de
instruções, teríamos que fazer um if com um bloco com mais de 8.000 linhas de código. Isso é algo extremamente
difícil de ocorrer em um programa real.
Mas caso você realmente precise pular mais de 32 000 instruções, isso é perfeitamente possível, desde que você
altere antes o valor do registrador PC. Com isso, perde-se um pouco de desempenho, mas torna desvios de código
extremamente grandes possível. E o importante é que o caso comum (programas em C com blocos menores de 8.000
linhas) roda mais rápido (regra 4 da Filosofia de Projeto do MIPS).
Além do beq, temos também o bne, ou Branchif Not Equal:
bne $r1, $r2, DESTINO
Ele funciona da mesma forma que o beq. A diferença é que ele pula um determinado número de instruções somente
se o valor dos dois registradores for diferente.
E finalmente, temos a instrução j, ou Jump:
j ENDEREÇO
Ele faz com que o programa passe a executar a instrução que é encontrada no endereço dado. O endereço passado
para a instrução j é sempre um número de 26 bits. Entretanto, os endereços da máquina sempre são números de 32
bits. Então como será possível saber qual o endereço em que está a instrução desejada?
É muito simples. Instruções sempre estarão em endereços múltiplos de 4. Então sabemos que os dois últimos bits do
endereço são sempre 0. Não precisamos armazená-los. Já os dois primeiros bits, retiramos do PC, pois é sempre
muito mais provável que nós usemos o controle de fluxo para saltar para uma posição não muito distante. Caso
precisemos saltar para um posição muito distante, basta alterarmos o valor do PC primeiro. Da mesma forma que foi
visto no beq, o caso comum executa mais rápido desta forma. Isso é muito bom, mesmo que tenhamos mais trabalho
e perda de desempenho nas pouquíssimas vezes nas quais precisamos dar saltos muito longos.
Instruções do MIPS 9
Instruções de Comparações
Por fim, precisamos ver ainda instruções de comparação. Um exemplo é o slt, ou Set Less Than:
slt $r1, $r2, $r3
Ela armazena 1 em $r1 se $r2 < $r3 e 0 caso contrário.
Resumo dos Modos de Endereçamento
Nas instruções do MIPS podemos representar os endereços de dados das seguintes formas:
• A Registrador: Representamos o dado passando o nome do registrador no qual ele está contido. Ex: add $r1,
$r2, $r2.
• Base-Deslocamento: Representamos o dado passando o endereço de um vetor no qual ele está e a quantidade de
bits a serem deslocados. Ex: lw $r5, 4($r65).
• Imediato: Passamos o dado escrevendo o seu valor imediato. Ex: addi $r1, $r2, 456.
• Relativo ao PC: Passamos o dado descrevendo o seu valor relativo ao endereço da instrução atual. Ex: beq $r1,
$r2, DESTINO.
• Absoluto: passamos o valor informando o seu endereço (pseudo-)absoluto. Ex: j DESTINO.
Existem apenas estas 5 formas de endereçamento no MIPS. Através delas, efetuamos todas as operações necessárias.
Referências
[1] http:/ / pages. cs. wisc. edu/ ~larus/ spim. html
Representação das Instruções
Sabemos que tudo dentro do computador é representado por meio de bits - que podem ter o valor de 0s e 1s. Até
mesmo as instruções de um processador MIPS. Vamos estudar agora como uma instrução é representada a nível de
bits. O MIPS reconhece 3 famílias diferentes de instruções. São elas:
As Instruções Tipo R
Como exemplo de instruções do tipo R vistas, temos: add, addu, sub, subu, or e and.
Elas são codificadas da seguinte forma:
[o][o][o][o][o][o] - [u][u][u][u][u] - [t][t][t][t][t] - [d][d][d][d][d] - [s][s][s][s][s] - [f][f][f][f][f][f]
Onde a letra "o", representa o opcode, ou Código de Operação. Ele avisa mais-ou-menos o que a instrução vai fazer.
Por exemplo, uma instrução de soma add é muito semelhante à instrução xor que faz um XOR lógico bit-a-bit. A
única diferença é que na soma, devemos armazenar um bit a mais para representar o "vai um" quando somamos 1+1.
Logo, as duas instruções tem o mesmo opcode.
As letras "u" e "t" representam os operandos das instruções. Ou melhor, os registradores nos quais estão os
operandos.
A letra "d" é o registrador de destino onde deve ser armazenado o resultado.
A letra "s" ou Shampt representa o deslocamento de bits, ou seja um shift que pode se usado após a operação.
Finalmente, a letra "f" é o código de função. É por meio dela que diferenciamos instruções semelhantes que tem o
mesmo opcode, como add e or.
Representação das Instruções 10
As Instruções Tipo I
Como exemplo deste tipo de instrução, podemos citar todas aquelas que contam com um valor imediato, como addi,
subi, ori, beq e bnq.
Eles são codificados da seguinte forma:
[o][o][o][o][o][o] - [u][u][u][u][u] - [t][t][t][t][t] - [i][i][i][i][i][i][i][i][i][i][i][i][i][i][i][i]
A letra "o" representa o código da instrução. A letra "u" represeenta o número do registrador onde a o resultado da
operação é colocado. Já a letra "t" representa o número do registrador em que está um dos operandos. Já o "i"
representa o número imediato. Agora vemos o porquê do valor passado como imediato nunca poder exceder os 16
bits.
As Instruções Tipo J
A única instrução vista no capítulo anterior do tipo J é a j. Ela é codificada da seguinte forma:
[o][o][o][o][o][o] - [d][d][d][d][d][d][d][d][d][d][d][d][d][d][d][d][d][d][d][d][d][d][d][d][d][d]
O "o" representa o código da operação j e d representa o destino.
As Pseudo-Instruções
A linguagem Assembly de uma máquina costuma ser um reflexo direto de como são implementadas as instruções de
um determinado processador. Entretanto, nem todas as instruções que temos à disposição quando programamos em
Assembly são instruções verdadeiras para o processador. Algumas delas são na verdade pseudo-instruções.
Pseudo-instruções costumam ser substituídas pelo montador ao gerar instruções para o computador na forma de
Lingüagem de Máquina. Pseudo-Instruções são na verdade combinações de mais de uma instrução. Vejamos agora
alguns exemplos:
A Pseudo-Instrução move
move $r1, $r2 # Copia o conteúdo do registrador $r2 para $r1
Ela é na verdade implementada da seguinte forma:
addu $r1, $r0, $r2 # Soma $r2 com zero e coloca o resultado em $r1
O registrador $r0 usado acima não é um registrador comum. Ele sempre possui o valor "0" e é um dos poucos
registradores cujo valor nunca pode ser alterado pelo programador.
As Pseudo-Instruções 11
A Pseudo-Instrução Load Address
la $r1, ENDEREÇO # Coloca o valor numérico de 32 bits "ENDEREÇO" em $r1
Esta instrução é muito útil para fazeer registradores receberem o valor de ponteiros para outros locais de memória.
De fato, usamos esta pseudo-instrução no Capítulo anterior quando convertemos um código em C para o Assembly
do MIPS. Ela é na verdade implementada desta forma:
lui $r1, constHI # Carrega-se os 16 bits mais significativos em $r1
ori $r1, $r0, constLO # Executa-se um OR bit-a-bit entre o registrador
 # com os 16 bits mais significativos e os 16 bits
 # menos significativos
O que a instrução lui, ou Load Upper Immediate faz é uma operação shift de 16 bits para a esquerda e coloca no
registrador indicado. Este passo é necessário porque valores imediatos passados para instruções só podem ter 16 bits
por causa da limitação de espaço das instruções do Tipo I.
Também existe a instrução li, ou Load Immediate que faz exatamente a mesma coisa que o la.
Note que o montador que remove as pseudo-instruções e as substitui por instruções que realmente existem no
hardware sempre podem verificar se o valor que queremos carregar no registrador cabe em 16 bits. Neste caso, a
instrução gerada fica bem mais simples e rápida:
addu $r1, $r0, $r2 # Soma $r2 com 0 e coloca em $r1
Suporte à Funções
Funções são ferramentas muito importantes, pois tornam código escrito muito mais usável e torna a tarefa de
programar mais produtiva. Elas permitem que um programador se concentre em apeenas uma tarefa de cada vez.
Portanto, é essencial que um processador possua suporte á elas. Veremos como isso ocorre no MIPS.
As Etapas Necessárias para Invocar Funções
Quando queremos chamar uma função, as seguintes etapas devem ser cumpridas:
•• O programa principal deve colocar os parâmetros da função em um local que ela possa acessar.
•• O programa principal deve ceeder o controle para a função.
•• A função deve coletar todos oos parâmetros deixados pelo programa principal.
•• A função deve executar a tarefa desejada.
•• A função deve aramazenar seus resultados em um lugar em que o programa principal possa acessar.
•• A função deve retornar o fluxo do código parao ponto imediatamente após ser chamada.
Para que estes requisitos sejam cumpridos, as seguintes convenções fora criadas:
• Os registradores $r4, $r5, $r6 e $r7 seriam usados para armazenar parâmetros de funções. Por causa desta
funcionalidade, tais registradores podem ser chamados pelos seus "apelidos": $a0, $a1, $a2 e $a3.
• Os registradores $r2 e $r3 seriam usados para as funções armazenarem seus valores de retorno para o programa
principal. Por isso, eles costumam ser chamados de $v0 e $v1.
Tais regras são apenas convenções. Nada impede que um programador as desrespeite e use outros registradores para
estabelecer comunicação entre funções e programas principais. Entretanto, para que um programa funcione bem em
conjunto com todos os demais, é importante quee tais convenções sejam seguidas.
Suporte à Funções 12
Além dos registradores convencionados acima, o registrador $r31 também tem um papel importante. Ele sempre
armazena o endereço de retorno para o qual a última função chamada deve retornar. Por ter uma função tão
importante, este registrador é mais conhecido pelo apelido $ra.
Instruções de Uso de Funções: jal e jr
A instrução que usamos para invocar uma função chama-se jal, ou Jump and Link. Ela é usada da seguinte forma:
jal ENDEREÇO_DA_FUNÇÃO
Entretanto, só devemos chamar esta função depois de já termos salvo os argumentos nos registradores apropriados.
Depois da função executar todas as operações e salvar os resultados apropriados em $v0 e $v1, podemos usar a
instrução jr, ou Jump Register. Normalmente usamos a instrução passando para ela o valor do registrador $ra, que
contém o endereço certo para voltarmos:
jr $ra
Como exemplo de como isso pode ser feito, vamos converter para Assembly o seguinte código em C:
int example(int a, int b, int c, int d){
 int f;
 f = (a + b) - (c + d)
 return f;
}
O código no Assembly do MIPS ficaria assim:
example: # Label
 add $t0, $a0, $a1 # Soma a + b
 add $t1, $a2, $a3 # Soma c + d
 sub $v0, $t0, $t1 # Subtrai os dois valores e coloca o resultado no registrador de retorno
 jr $ra # Retorna o controle para a função principal
Entretanto, observe que para realizarmos os cálculos necessários, precisamos usar registradores. Ao fazer isso, existe
o grande risco de apagarmos dados importantes do programa principal. Para evitar isso, existe uma convenção que
diz que os registradores $r8 até $r15, $r24 e $r25 são registradores temporários. Por isso, eles costumam ser
chamados de $t0 até $t9. Após chamar uma função, o programa principal não deve esperar que os valores destes
registradores sejam os mesmos. Somente dados que estejam nos registradores $r16 até $r23 são sempre preservados
entre funções. Por esta razão, tais registradores permanentes (salvos) são chamados de $s0 até $s7.
Funções com Muitas Variáveis
Existem funções que podem precisar receber mais do que 4 parâmetros. Entretanto, só temos registradores
suficientes para armazenar 4 parâmetros. Nós também podemos querer retornar mais do que os 2 valores permitidos
pelos registradores. Ou então, nossa função pode precisar armazenar mais do que 10 valores temporários. O que
fazer para resolver isso?
Como o espaço dentro de registradores é bastante limitado, só nos resta apelar para a pilha da memória. Por
convenção, o registrador $r29, ou $sp (Stack Pointer) armazena o endereço na pilha de onde novos valores podem
ser colocados. Acessando seu endereço e alterando-o podemos guardar valores na memória. O acesso à pilha da
memória é muito mais lento que o acesso à registradores, mas este é o único modo de podermos armazenar uma
quantidade muito maior de informação.
Suporte à Funções 13
Por motivos históricos, a pilha "cresce" sempre dos valores de endereços altos para valores menores. Ou seja, para
alocar espaço para mais valores, precisamos sempre decrementar o seu valor. Incrementando-o, estamos na verdade
removendo os últimos dados da pilha.
Vamos reescrever agora o código Assembly visto acima, desta vez preeservando os valores dos registradores $t0 e
$t1 para vermos como ficaria o resultado:
example: # Label
 addi $sp, $sp, -8 # Alocamos espaço para dois valores de 4 bits.
 sw $t1, 4($sp) # Preservamos o valor de $t1 à partir do 4o bit após o Stack Pointer
 sw $t0, 0($sp) # Preservamos o valor de $t0 sobre o Stack Pointer
 add $t0, $a0, $a1 # Soma a + b
 add $t1, $a2, $a3 # Soma c + d
 sub $v0, $t0, $t1 # Subtrai os dois valores e coloca o resultado no registrador de retorno
 lw $t0, 0($sp) # Retiramos o valor antigo salvo de $t0
 lw $t1, 4($sp) # Retiramos da pilha o valor antigo de $t1
 addi $sp, $sp, 8 # Voltamos o Stack Pointer para a posição original apagando de vez as variáveis locais
 jr $ra # Retorna o controle para a função principal
Funções Recursivas e Sub-Funções
Da mesma forma que o programa principal invocou uma função, uma função também pode invocar outras funções.
Se la for recursiva, ela pode até mesmo invocar clones de si mesma para realizar uma tarefa. Como implementar isso
sendo que temos apenas um registrador $ra? Se chamarmos outra função, o $ra original é sobrescrito e podemos
perder a capacidade de voltar ao programa principal. Além disso, valores temporários que representam variáveis
locais podem ser perdidos.
A única forma de evitar isso é enviar para a pilha tudo aquilo que precisa ser salvo - da mesma forma que fizemos
com alguns valores no exemplo acima. Para mostrar isso na prática vamos implementar em Assembly a seguinte
função em C:
/* Calcula Fatorial */
int fat(int n){
 if(n < 1)
 return 1;
 else
 return (n * fat(n - 1));
}
O resultado final é algo como:
fat: # Início da função
 addi $sp, $sp, -8 # Aloca espaço na pilha para 2 valores
 sw $ra, 4($sp) # Guarda valor de retorno na pilha
 sw $a0, 0($sp) # Guarda primeiro argumento na pilha
 slti $t0, $a0, 1 # $t0 = ( $a0 < 1)
 beq $t0, $zero, L1 # Se $a0 >= 1, vá para L1
 addi $v0, $zero, 1 # Senão, coloque 1 como valor de retorno
 addi $sp, $sp, 8 # Apague as duas variáveis locais salvas na pilha
 jr $ra # Encerra a função
L1: addi $a0, $a0, -1 # Se $a >= 1, decremente $a0
Suporte à Funções 14
 jal fat # Chame um clone recursivo de fat que retorna fat($a0-1)
 lw $a0, 0($sp) # Recupere os valores iniciais do parâmetro da função
 lw $ra, 4($sp) # Recupere o endereço de retorno antigo
 addi $sp, $sp, 8 # Apague as duas variáveis locais salvas na pilha
 mul $v0, $a0, $v0 # Coloque $a0 * fat($a0 - 1) como valor de retorno
 jr $ra # Encerra a função
Por fim, uma última coisa útil que é interessante comentar é que o Stack Pointer ($sp) pode ser alterado várias vezes
ao longo de uma função. Para manter memorizado o valor do endereço da pilha no início da função, costuma-se usar
o registrador $r30 como um Frame Pointer ($fp). Nos exemplos acima, isso não foi preciso, pois só mudamos o
valor do Stack Pointer no começo e fim de cada função. Mas em alguns programas utilizar este registrador pode ser
útil.
Representação Numérica
Números podem ser representados em qualquer tipo de base. Humanos costumam representar números na base
decimal. Esta possui este nome por possuir 10 dígitos diferentes: 0, 1, 2, 3, 4, 5, 6, 7, 8 e 9.
Como computadores só são capazes de reconhecer dois tipos diferentes de estados, é natural para eles representar
números na base binária. Ou seja, eles só compreendem os valores "0" e "1". Vamos ver agora coo podemos
representar diversos tipos de números usando apenas dois tipos diferentes de sinais.Números Naturais
Representar Números Naturais é simples bastante simples - mesmo quando só podemos usar dois tipos diferentes de
sinais. O 0 decimal é representado como 0. O 1 decimal também é representado como 1. Já o 2 decimal é
representado com "10". Enfim, quando contamos os números naturais na base binária, fazemos da seguinte forma:
BASE |
BINÁRIA | 0 1 10 11 100 101 110 111 1000 1001 1010
 |
BASE |
DECIMAL | 0 1 2 3 4 5 6 7 8 9 10
Quando representamos números desta forma, não temos números negativos. Logo, nenhum número possui sinal
algum. Por isso, chamamos tais números de unsigned (sem sinal).
Atualmente, a maioria das máquinas possui representação numérica 32 bits. Isso significa que seus números são
compostos por 32 dígitos. Com uma representação destas, o menor número binário sem sinal que podemos
representar é 00000000000000000000000000000000 e o maior é 1111111111111111111111111111111.
Convertemos tais valores para decimal, chegamos à conclusão que a maioria dos computadores só lida com números
sem sinal cujo valor esteja entre 0 e 4.294.967.295 - que são os equivalentes decimais destes números.
Para descobrir qual é o equivalente decimal de um número binário de 32 bits, usa-se a fórmula abaixo:
onde é o dígito mais significativo e é o bit menos significativo.
Representação Numérica 15
Números Inteiros
Nem sempre os números com os quais queremos lidar são naturais. Pode haver necessidade de realizarmos operações
com números negativos. Para tal, devemos encontrar uma forma de representarmos números negativos. Uma das
primeiras formas tentadas de fazer isso foi reservando o primeiro bit de um número para representar o sinal. Assim,
um 0000000 (...) 001 representa +1 e um 10000 (...) 001 representaria um -1. Entretanto, tal forma de representação
já foi abandonada há muito tempo. Um dos principai motivos para o abandono desta representação está no fato de
sempre termos que verificar o primeiro bit para descobrir como efetuar uma soma ou subtração entre dois números.
Além disso, tal representação tornaria possível um "+0" e um "-0".
A forma de se representar números inteiros em computadores modernos é chamada de Representação em
Complemento de 2. Ela é feita da seguinte forma:
Os 31 bits mais à esquerda representam sempre números positivos. Calculamos o que eles representam com a mesma
fórmula vista acima. Entretanto, o primeiro bit representa sempre "0" se o seu valor for "0" ou " se o seu valor
for "1". Assim, a fórmula para se calcular o que representa um número binário inteiro em decimal é:
onde é o dígito mais significativo e é o bit menos significativo.
A representação em complemento de 2 é boa pelos seguintes motivos:
•• Para somar dois números inteiros, usa-se o mesmo método. Não precisamos verificar o sinal deles. Com isso,
podemos criar um circuito mais rápido.
•• Descobrir o inverso de um número também é simples. Basta invertermos todos os bits e, em seguida, somarmos 1.
Sempre funciona.
•• O "0" é sempre "0". Não existe uma versão negativa ou positiva deste número.
Por meio desta notação, números tão pequenos como -4.294.967.296 e tão grandes como 4.294.967.295 podem ser
representados.
Números Reais
Por fim, também é muito útil que computadores possam representar números reais. Vamos estudar agora como eles
costumam ser representados em computadores.
A forma mais lógica e versátil de representarmos um número real é por meio da notação científica. Um número
decimal em notação científica sempre toma a seguinte forma:
onde N é sempre um número real maior ou igual à 1 e menor que 10 que pode ser positivo ou negativo enquanto M é
um número inteiro qualquer (que pode ser positivo ou negativo). Por exemplo:
representa aproximadamente quantos segundos existe em um século enquanto
representa quantos segundos existem em um nanossegundo.
Alternativamente, podemos representar também números em notação científica utilizando uma base binária:
. Lembre-se que aquele "10" significa na verdadee "2" em representação decimal. Ou seja, o
número representado é na verdade 0,5 e não 0,1.
Enfim, quando temos um número em notação científica binária, ele sempre tem a seguinte forma:
, onde "S" representa o sinal (que pode ser positivo ou negativo), "XXXXX" são
a parte fracionária do número e "YYYYYY" representa o expoente.
Então, para representarmos números reais, precisamos representar no espaço de 32 bits os valores do sinal, fração e
expoente. No MIPS, isso é feito da seguinte forma:
Representação Numérica 16
S -- E E E E E E E E -- F F F F F F F F F F F F F F F F F F F F F F F 
O Sinal é representado em um único bit (1 para negativo e 0 para positivo), o expoente é representado por 8 bits e a
fração é representada por 23 bits. Assim, o exemplo (ou 1/2 em decimal) é representado:
0 -- 1 1 1 1 1 1 1 1 -- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Com esta representação, números quase tão pequenos como ou quase tão grandes como podem ser
armazenados.
Entretanto, note que a nossa precisão não é perfeita. Existem muitos números que não podem ser representados desta
forma. Por isso, sempre é preciso realizar arredondamentos. E quanto mais operações realizamos, mais erros acabam
sendo acumulados. Por essa razão, a maioria dos computadores possui também uma forma mais precisa de
representação de números reais. Essa forma normalmente ocupa o dobro do espaço do que costuma ocupar. No
MIPS uma representação desti tipo ocupa 64 bits. Ela funciona da seguinte forma:
S -- E E E E E E E E E E E -- F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F
Apesar de gastarmos o dobro do espaço de armazenamento desta forma, aumentamos consideravelmente a faixa de
valores representados e a precisão com a qual podemos representá-los.
Overflow e Underflow
Overflow é o nome do resultado incorreto ao qual chegamos quando tentamos somar números grandes demais e o
resultado não pode ser armazenado e nem representado em 32 bits. Ele pode ocorrer quando efetuamos as seguintes
operações:
•• Somamos dois números positivos
•• Somamos dois números negativos
•• Subtraímos um número negativo de um positivo.
É de responsabilidade do hardware sempre verificar se ocorreu um overflow nestes casos. Normalmente, para fazer a
verificação basta conferir os sinais dos números. Em caso afirmativo, ele deve ativar um sinal de exceção. O que é
feito caso uma excessão seja detectada depende de cada programa.
O MIPS, por exemplo, sempre armazena o endeereço da última instrução que gerou excessão em um registrador
especial chamado EPC. A instrução mfc0 (Move from System Control) é usada para copiar este endereço para um
registrador de propósito geral que pode ser conferido por algum programa. Cabe ao programador deste programa
decidir o que fazer.
Em lingüagens de alto nível, o comportamento depende da lingüagem usada. C, por exemplo, ignora todos os
Overflows. Ao contrário de Fortran ou Ada que requerem que o programa sempre seja avisado.
Underflow é o que acontecee quando lidamos com um número real tão próximo de 0 que o valor do expoente não
pode ser corretamente representado. Quando um programa requer uma precisão tão grande assim, recomenda-se o
uso de pontos flutuantes de dupla precisão (double) para evitar este inconveniente.
As Operações da Unidade Lógica Aritmética 17
As Operações da Unidade Lógica Aritmética
Computadores são máquinas de calcular. Logo, é fundamental que eles sejam capazes de realizar operações básicas
como adição, subtração, divisão e multiplicação. Vamos ver agora como tais operações são implementadas dentro da
Unidade Lógica Aritmética de um Processador (ULA).
A Adição e Subtração
A Adição é uma das operações mais simples. Um computador realiza ela de uma maneira semelhante à usada por
nós humanos. Ele começa somando os bits menos significativos. Caso tenha que somar "1"e "1", o resultado fica
sendo "0" e passamos um "vai-um" para o bit à esquerda. Veja abaixo um desenho de um circuito capaz de somar
dois números de 8 bits:
Embora a imagem mostre apenas um circuito que soma dois números de 8 bits, não é muito difícil perceber que a
lógica para fazer um somador é a mesma, tanto para números com 8 como para 32 bits. Basta adicionar mais
somadores. Perceba que o circuito também é capaz de detectar a presença de Overflow no caso de operações com
números sem sinal.
Ele pode ser usado tanto para somar números com ou sem sinal. Graças à representação de números inteiros por
complemento de dois, não é difícil conseguir isso.
O mesmo circuito acima também pode ser reaproveitado para realizar subtrações. Basta inverter antes todos os bits
do segundo operando e somar 1 à ele. Com isso, estamos na verdade somando o primeiro operando com o negativo
do segundo operando.
As Operações da Unidade Lógica Aritmética 18
A Multiplicação
Vamos agora estudar como construir um circuito capaz de realizar multiplicações. Primeiro vamos tentar realizar
uma multiplicação pelo método convencional que usamos através de papel e caneta. Isso pode nos dar uma pista de
como fazer uma operação de multiplicação. Vamos usar a base binária:
 1000
 x1001
 ----
 1000
 00000
 000000
1000000
-------
1001000
Perceba que a multiplicação é um conjunto de somas. Sempre estamos somando o valor 0 ou o valor do
multiplicador após este passar por uma operação de SHIFT para a esquerda. O que determina se o que iremos somar
é um 0 ou o valor do multiplicador após um SHIFT são os bits do multiplicando. Se o bit da posição n for um 0,
somamos 0 e se for 1, somamos com o valor do multiplicando "shiftado" n vezes.
Um exemplo de um circuito seqüencial capaz de somar dois números de 4 bits é mostrado na imagem abaixo:
Note que multiplicando números de 4 bits, nós precisamos armazenar os 8 bits possíveis para o resultado. Afinal, o
resultado de uma multiplicação é capaz de ter o número de bits igual à soma dos multiplicandos. Máquinas MIPS
lidam com o problema dividindo o resultado pela metade. os bits mais significativos terminam em um registrador
chamado Hi e os menos significativos acabam em um registrador chamado Lo.
Entretanto, o circuito conforme mostrado acima tem muitos problemas. O principal é que ele é muito complexo.
Perceba que o circuito para multiplicar número de 4 bits é pelo menos 3 vezes mais complexo e lento que o usado
para realizar a adição de 8 bits (afinal, o circuito de multiplicação precisa realizar a adição de 3 pares de valores de 8
bits). Um circuito destes para calcular a multiplicação de números com um valor ainda maior de bits seria ainda mais
complexo que isso.
Com isso, chegamos à conclusão que a multiplicação é uma operação complexa demais para ser efetuada por meio
de uma simples lógica combinacional de portas lógicas. Um circuito de multiplicação combinacional seria muito
caro e esquentaria demais. Por isso, sua velocidade teria que ser sacrificada aumentando a distância dos transistores.
Por isso, utilizam-se circuitos seqüenciais para realizar esta operação.
As Operações da Unidade Lógica Aritmética 19
O diagrama abaixo mostra descreve o funcionamento de um circuito seqüencial capaz de realizar a multiplicação:
O que acontece no diagrama acima no caso de multiplicarmos dois números de 32 bits é o seguinte:
• 1- O "Produto" é um registrador inicializado em 0.
• 2- O produto e o multiplicando são passados para a ULA que soma os dois.
• 3- O resultado (o valor do próprio multiplicando) pode ir para o registrador "Produto" ou não. Quem toma a
decisão é o "Controle".
• 4- Se o último bit do Multiplicador for "1", o Controle permite a escrita do registrador "Produto". Se for "0", ele
impede.
• 5- O Multiplicando sofre um shift para a esquerda e o Multiplicador um Shift para a direita.
• 6- Se é a 32a vez que você chega à esta instrução, encerre. A multiplicação terminou. Caso contrário, volte ao
passo 2.
O diagrama acima já é uma forma bem melhor de multiplicarmos. Mas ainda existem otimizaçãoes que podem ser
feitas para acelerarmos ainda mias a multiplicação:
•• Podemos utilizar um único registrador de 64 bits para armazenar tanto o multiplicador como o produto. O
multiplicador vem antes e o produto logo depois. Assim, cada vez que fazemos um shift para a direita no
multiplicador, aproveitamos o espaço liberado mais à esquerda para colocar um novo bit do produto. Desta forma,
não é necessário somar números de 32 bits, apenas somar o suficiente para descobrir qual o próximo bit do
produto a ser colocado.
•• Compiladores substituem multiplicação por potências de 2 por operações de shift que são mais rápidas.
Para multiplicações envolvendo números negativos, basta invetermos os números transformando-os em positivos.
Em seeguida, fazemos a multiplicação normalmente e observamos os sinais do multiplicador e multiplicando. Se
forem iguais, masntemos os números como positivos. Caso contrário, convertemos o produto para negativo.
Fontes e Editores da Página 20
Fontes e Editores da Página
Capa  Fonte: http://pt.wikibooks.org/w/index.php?oldid=204857  Contribuidores: Master, Raylton P. Sousa, 1 edições anónimas
Introdução  Fonte: http://pt.wikibooks.org/w/index.php?oldid=239231  Contribuidores: Master, Raylton P. Sousa, Thiagoharry, 5 edições anónimas
O que é o MIPS?  Fonte: http://pt.wikibooks.org/w/index.php?oldid=199789  Contribuidores: GrooveDog, Master, Raylton P. Sousa, Thiagoharry, 9 edições anónimas
Instruções do MIPS  Fonte: http://pt.wikibooks.org/w/index.php?oldid=250818  Contribuidores: Kernelsafe, Master, Raylton P. Sousa, Thiagoharry, 5 edições anónimas
Representação das Instruções  Fonte: http://pt.wikibooks.org/w/index.php?oldid=199791  Contribuidores: Master, Raylton P. Sousa, Thiagoharry
As Pseudo-Instruções  Fonte: http://pt.wikibooks.org/w/index.php?oldid=199786  Contribuidores: Master, Mike.lifeguard, Raylton P. Sousa, Thiagoharry, 1 edições anónimas
Suporte à Funções  Fonte: http://pt.wikibooks.org/w/index.php?oldid=239421  Contribuidores: Master, Raylton P. Sousa, Thiagoharry, 3 edições anónimas
Representação Numérica  Fonte: http://pt.wikibooks.org/w/index.php?oldid=199790  Contribuidores: Master, Raylton P. Sousa, Thiagoharry, 3 edições anónimas
As Operações da Unidade Lógica Aritmética  Fonte: http://pt.wikibooks.org/w/index.php?oldid=199785  Contribuidores: Master, Raylton P. Sousa, Thiagoharry
Fontes, Licenças e Editores da Imagem 21
Fontes, Licenças e Editores da Imagem
Imagem:Cray-1-deutsches-museum.jpg  Fonte: http://pt.wikibooks.org/w/index.php?title=Ficheiro:Cray-1-deutsches-museum.jpg  Licença: Creative Commons Attribution 2.5  Contribuidores:
Clemens PFEIFFER
Imagem:Transistor_npn.png  Fonte: http://pt.wikibooks.org/w/index.php?title=Ficheiro:Transistor_npn.png  Licença: GNU Free Documentation License  Contribuidores: Original uploader was
Pulsar at fr.wikipedia
Imagem:Logic-gate-index.png  Fonte: http://pt.wikibooks.org/w/index.php?title=Ficheiro:Logic-gate-index.png  Licença: GNU Free Documentation License  Contribuidores: EugeneZelenko,
Ilmari Karonen, Jjbeard, Qvarie, Stefan506, The wub, Tothwolf, WikipediaMaster, 2 edições anónimas
Imagem:ALU_Block_Diagram.png  Fonte: http://pt.wikibooks.org/w/index.php?title=Ficheiro:ALU_Block_Diagram.png  Licença: Creative Commons Attribution-Share Alike  Contribuidores:
Tapani Taivalantti
Imagem: Kernel_Layout.svg  Fonte: http://pt.wikibooks.org/w/index.php?title=Ficheiro:Kernel_Layout.svg  Licença: Creative Commons Attribution-Sharealike 3.0  Contribuidores: Bobbo
Imagem:Toshiba_TC86R4400MC-200_9636YJA_top.jpg  Fonte: http://pt.wikibooks.org/w/index.php?title=Ficheiro:Toshiba_TC86R4400MC-200_9636YJA_top.jpg  Licença: GNU Free
Documentation License  Contribuidores: EugeneZelenko, Morkork, Qurren, Sdrtirs, 3 edições anónimas
Imagem:Circuito_soma.pngFonte: http://pt.wikibooks.org/w/index.php?title=Ficheiro:Circuito_soma.png  Licença: GNU Free Documentation License  Contribuidores: Thiagoharry
Imagem:Circuito_multiplicacao.png  Fonte: http://pt.wikibooks.org/w/index.php?title=Ficheiro:Circuito_multiplicacao.png  Licença: GNU Free Documentation License  Contribuidores:
Thiagoharry
Imagem:Diagrama_circuito_multiplicacao.png  Fonte: http://pt.wikibooks.org/w/index.php?title=Ficheiro:Diagrama_circuito_multiplicacao.png  Licença: Creative Commons
Attribution-Sharealike 3.0  Contribuidores: Thiagoharry
Licença 22
Licença
Creative Commons Attribution-Share Alike 3.0
//creativecommons.org/licenses/by-sa/3.0/

Continue navegando