Buscar

Arquitetura de Processadores

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 75 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 75 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 75 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

Capítulo 8 
Arquitetura de
processadores 
Mostraremos neste capítulo alguns conceitos importantes sobre o
funcionamento interno dos processadores. Tomaremos como exemplo os
processadores Intel, e com eles você entenderá conceitos como execução
especulativa, pipeline, previsão de desvio, paralelismo, micro-operações,
linguagem assembly, memória virtual, paginação e outros termos complexos.
O assunto é difícil, mas vale a pena, pois o leitor passará a ter um
conhecimento mais profundo sobre o que se passa dentro de um
processador. 
Registradores internos do processador
Para entender como um processador executa programas, precisamos
conhecer a sua arquitetura interna, do ponto de vista de software. Dentro de
um processador existem vários circuitos chamados de registradores. Os
registradores funcionam como posições de memória, porém o seu acesso é
extremamente rápido, muito mais veloz que o da cache L1. O número de
bits dos registradores depende do processador. 
 Processadores de 8 bits usam registradores de 8 bits
 Processadores de 16 bits usam registradores de 16 bits
 Processadores de 32 bits usam registradores de 32 bits
 Processadores de 64 bits usam registradores de 64 bits
A figura 1 mostra os registradores internos dos processadores 8086, 8088 e
80286, todos de 16 bits. Todos os processadores têm uma linguagem baseada
8-2 Hardware Total
em códigos numéricos na memória. Cada código significa uma instrução. Por
exemplo, podemos ter uma instrução para somar o valor de AX com o valor
de BX e guardar o resultado em AX. As instruções do processador que
encontramos na memória são o que chamamos de linguagem de máquina.
Nenhum programador consegue criar programas complexos usando a
linguagem de máquina, pois ela é formada por códigos numéricos. É
verdade que alguns programadores conseguem fazer isso, mas não para
programas muito longos, pois tornam-se difíceis de entender e de gerenciar.
Ao invés disso, são utilizados códigos representados por siglas. As siglas são
os nomes das instruções, e os operandos dessas instruções são os
registradores, valores existentes na memória e valores constantes. 
Figura 8.1
Registradores internos do
processador 8086.
Por exemplo, a instrução que acabamos de citar, que soma o valor dos
registradores AX e BX e guarda o resultado em AX, é representada por:
ADD AX,BX
Esta instrução é representada na memória pelo seguinte código de máquina:
01 D8
Portanto a instrução ADD AX,BX é representada na memória por dois
bytes, com valores 01 e D8 (em hexadecimal). Os bytes na memória que
formam os programas são o que chamamos de linguagem de máquina. Esse
códigos são lidos e executados pelo processador. Já as representações por
siglas, como “ADD AX,BX”, formam o que chamamos de linguagem
assembly. Quando programamos em linguagem assembly, estamos utilizando
Capítulo 8 – Arquitetura de processadores 8-3
as instruções nativas do processador. A linguagem assembly é usada para
escrever programas que têm contato direto com o hardware, como o BIOS e
drivers. O assembly também é chamado linguagem de baixo nível, pois
interage intimamente com o hardware. Programas que não necessitam deste
contato direto com o hardware não precisam ser escritos em assembly, e são
em geral escritos em linguagens como C, Pascal, Delphi, Basic e diversas
outras. Essas são chamadas linguagens de alto nível. Nas linguagens de alto
nível, não nos preocupamos com os registradores do processador, nem com
a sua arquitetura interna. Os programas pensam apenas em dados, matrizes,
arquivos, telas, etc. 
Apresentamos abaixo um pequeno trecho de um programa em linguagem
assembly. Em cada linha deste programa temos na parte esquerda, os
endereços, formados por duas partes (segmento e offset). A seguir temos as
instruções em códigos de máquina, e finalmente as instruções em assembly. 
Endereço Código Assembly
--------- ------------- ------------------------------
1B8D:0100 01D8 ADD AX,BX
1B8D:0102 C3 RET
1B8D:0103 16 PUSH SS
1B8D:0104 B03A MOV AL,3A
1B8D:0106 380685D5 CMP [D585],AL
1B8D:010A 750E JNZ 011A
1B8D:010C 804E0402 OR BYTE PTR [BP+04],02
1B8D:0110 BF86D5 MOV DI,D586
1B8D:0113 C6460000 MOV BYTE PTR [BP+00],00
1B8D:0117 E85F0B CALL 0C79
1B8D:011A 8B7E34 MOV DI,[BP+34]
1B8D:011D 007C1B ADD [SI+1B],BH
Quando estamos programando em linguagem assembly, escrevemos apenas
os nomes das instruções. Depois de escrever o programa, usando um editor
de textos comum, usamos um programa chamado compilador de linguagem
assembly, ou simplesmente, Assembler. O que este programa faz é ler o
arquivo com as instruções (arquivo fonte) e gerar um arquivo contendo
apenas os códigos das instruções, em linguagem de máquina (arquivo
objeto). O arquivo objeto passa ainda por um processo chamado link edição,
e finalmente se transforma em um programa, que pode ser executado pelo
processador. O Assembler também gera um arquivo de impressão, contendo
os endereços, códigos e instruções em assembly, como no trecho de listagem
que mostramos acima. O programador pode utilizar esta listagem para
depurar o programa, ou seja, testar o seu funcionamento. 
8-4 Hardware Total
Os códigos hexadecimais que representam as instruções do processador são
chamados de opcodes. As siglas que representam essas instruções são
chamadas de mnemônicos. 
Daremos neste capítulo, noções básicas da linguagem assembly dos
processadores modernos. Não ensinaremos a linguagem a fundo, mas o
suficiente para você entender como os processadores trabalham. Como a
programação nos processadores modernos é relativamente complexa,
começaremos com o 8080, de 8 bits. A arquitetura do 8080 deu origem à do
8086, que por sua vez deu origem ao 386 e aos processadores modernos.
Entendendo o 8080, que é bem mais simples, será mais fácil entender os
processadores modernos. 
Linguagem Assembly 8080
Aprender assembly do 8080 não é uma inutilidade, por duas razões.
Primeiro porque você entenderá com muito mais facilidade o assembly dos
processadores modernos, que afinal foram inspirados no 8080. Segundo que
nem só de PCs vive um especialista em hardware. Você poderá trabalhar
com placas controladoras que são baseadas nos processadores 8051 e Z80.
Ambos são de 8 bits e também derivados do 8080, e são bastante utilizados
em projetos modernos. 
A figura 2 mostra os registradores internos do 8080. São registradores de 8
bits, com exceção do PC (Program Counter) e do SP (Stack Pointer), que
têm 16 bits. 
Figura 8.2
Registradores internos do 8080.
O registrador mais importante é o acumulador. Ele é o valor de saída da
unidade lógica e aritmética (ALU), na qual são realizadas todas as operações.
Processadores atuais permitem fazer operações com todos os registradores,
mas no 8080, o acumulador deve obrigatoriamente ter um dos operandos, e
sempre é onde ficam os resultados. 
Os registradores B, C, D, E, H e L são de uso geral. Servem como operandos
nas operações lógicas e aritméticas envolvendo o acumulador. O PC é um
registrador de 16 bits, e seus valores são usados para formar o barramento de
Capítulo 8 – Arquitetura de processadores 8-5
endereços do processador durante as buscas de instruções. O PC tem
sempre o endereço da próxima instrução a ser executada. 
O SP (Stack Pointer) é muito importante. Ele serve para endereçar uma área
de memória chamada stack (pilha). A pilha serve para que os programas
possam usar o que chamamos de subrotinas, que são trechos de programa
que podem ser usados em váriospontos diferentes. Por exemplo, se em um
programa é preciso enviar caracteres para o vídeo, não é preciso usar em
vários pontos deste programa, as diversas instruções que fazem este trabalho.
Basta fazer uma subrotina com essas funções e “chamá-la” onde for
necessária. A subrotina deve terminar com uma instrução RET, que faz o
programa retornar ao ponto no qual a subrotina foi chamada. Para chamar
uma subrotina, basta usar a instrução CALL. Quando esta instrução é
executada, é automaticamente armazenado na pilha, o endereço da instrução
imediatamente posterior à instrução CALL (endereço de retorno). Subrotinas
podem chamar outras subrotinas, permitindo assim criar programas mais
complexos. O Stack Pointer sempre aponta para o topo da pilha, e é
automaticamente corrigido à medida em que são usadas instruções CALL e
RET. A instrução RET consiste em obter o endereço de retorno existente no
topo da pilha e copiá-lo para o PC (Program Counter). Isso fará com que o
programa continue a partir da instrução posterior à instrução CALL. 
Os FLAGS são um conjunto de 8 bits que representam resultados de
operações aritméticas e lógicas. São os seguintes esses bits:
Símbolo Nome Descrição
Z Zero Indica se o resultado da operação foi zero
CY Carry Indica se uma operação aritmética teve “vai um” ou “pede emprestado”
P Parity Indica a paridade do resultado da operação.
S Signal Indica o sinal de uma operação, se foi positivo ou negativo
AC Aux. Carry Carry auxiliar, em algumas instruções especiais. 
Apesar de ser um processador de 8 bits, o 8080 é capaz de realizar algumas
operações de 16 bits. Nessas operações, os registradores B e C são tratados
como um valor de 16 bits. O mesmo ocorre com o par D/E e H/L. 
Além de manipular os registadores, o 8080 também permite obter valores na
memória. Esses valores podem ser de 8 ou 16 bits, e nas instruções que
fazem esses acessos, basta indicar o endereço de 16 bits da posição de
memória que desejamos acessar. Além disso é possivel usar os registradores
HL, BC e DE como apontadores para posições de memória. Nas instruções
do assembly do 8080, o pseudo registrador M é na verdade a posição de
memória (8 bits) cujo endereço está em HL. 
8-6 Hardware Total
Programar em assembly do 8080 consiste em utilizar suas instruções,
manipulando seus registradores para executar as funções que desejamos. 
Instruções de movimentação de dados
MOV: Move dados entre dois registradores diferentes. Assim como na
maioria das instruções que envolvem registradores, podemos usar M como
sendo a posição de memória apontada por HL. Exemplos:
MOV A,C ; A=C
MOV C,E ; C=E
MOV D,M ; D=M, ou seja, a posição de memória indicada
; por HL
MOV M,A ; M=A
Note que quando escrevemos programas em assembly, podemos usar
comentários em cada linha, bastando usar um ponto-e-vírgula após a
instrução. Tudo o que estiver depois do ponto-e-vírgula será ignorado pelo
assembler. Aqui aproveitamos este convenção para colocar também
comentários explicativos nas instruções de nossos exemplos. 
MVI: Carrega um valor constante de 8 bits em um registrador de 8 bits ou
na posição de memória apontada por HL. Exemplos:
MVI C,200 ; Carrega o registrador C com 200 (decimal)
MVI A,15h ; Carrega o acumulador com 15 hexadecimal
MVI M,150 ; Armazena o valor 150 em [HL]
MVI L,32 ; Carrega o registrador L com 32 em decimal
Aproveitamos para além de exemplificar essas instruções, apresentar mais
algumas convenções usadas na linguagem assembly. Os números podem ser
representados nos formatos binário, octal, hexadecimal ou decimal. Quando
não usamos sufixos após os números, considera-se que são números
decimais. Para números hexadecimais, usamos o sufixo H. Quando um
número hexadecimal começa com A, B, C, E, E ou F, temos que usar um
“0” no início, para que o assembler não pense que se trata de uma variável, e
não um número. Números binários devem terminar com “b”, e números
octais devem terminar com “q”. Exemplos:
190 
10010111b
325q
8BC3h
Capítulo 8 – Arquitetura de processadores 8-7
Os quatro números acima estão expressos respectivamente em decimal,
binário, octal e hexadecimal. 
Outra convenção que vamos introduzir aqui é usar o símbolo [HL] para
indicar a posição de memória cujo endereço é dado por HL. Na linguagem
assembly do 8080, este é o papel do símbolo M. Não usamos [HL], porém
esta convenção foi adotada no assembly do 8086 e outros processadores mais
novos. Da mesma forma vamos usar os símbolos [BC] e [DE] para indicar as
posições de memória apontadas por BC e por DE. 
LXI: Carrega um valor constante de 16 bits em um dos pares BC, DE, HL e
no Stack Pointer. Exemplos:
LXI H,35AFh ; Carega HL com o valor 35AF hexadecimal
LXI D,25100 ; Carrega DE com o valor 25100 decimal
LXI B,0 ; Carrega BC com 0
LXI SP,200 ; Carrega o Stack Pointer com 200 decimal
Note que os números de 8 bits podem assumir valores inteiros positivos de 0
a 255 decimal (ou de 0 a FF em hexadecimal). Os números inteiros positivos
de 16 bits podem assumir valores entre 0 e 65.535 decimal (ou 0 a FFFF
hex). 
Obseve a instrução LXI H, 35AFh. Este valor 35AF é formado por 16 bits,
sendo que os 8 bits mais significativos têm o valor 35 hex, e os 8 bits menos
significativos têm o valor AF hex. No par HL, o registrador H é o mais
significativo, e o registrador L é o menos significativo. Sendo assim o
registrador H ficará com 35 hex e o registrador L ficará com AF hex. 
LDA e STA: A instrução LDA carrega o acumulador (registrador A) com o
valor que está no endereço de memória especificado. A instrução STA faz o
inverso, ou seja, guarda o valor de A na posição de memória especificada.
Exemplos:
LDA 1000h ; Carrega A com o valor existente em [1000h]
STA 2000h ; Guarda o valor de A em [2000h]
Estamos utilizando a partir de agora a notação [nnnn] para indicar a posição
de memória cujo endereço é nnnn. Esta notação não é usada no assembly
para 8080, mas é usada no assembly do 8086 e superiores. 
LHLD e SHLD: A instrução LHLD carrega nos registradores H e L, o valor
de 16 bits existente nas duas células de memória cujo endereço é
especificado. A instrução SHLD faz o inverso. Exemplos:
8-8 Hardware Total
LHLD 1000h ; Faz L=[1000h] e H=[1001h]
SHLD 2000h ; Guarda L em [2000h] e H em [2001h]
Aqui vai mais um conceito importante. A memória do 8080 é uma sucessão
de bytes, mas podemos também acessar words, ou seja, grupos de 16 bits. A
operação envolve dos bytes consecutivos, e nas instruções indicamos apenas
o endereço do primeiro byte. Os 8 bits menos significativos estão
relacionados com a posição de memória indicada, e os 8 bits seguintes estão
relacionados com a próxima posição. A figura 3 ilustra o que ocorre ao
usarmos a instrução SHLD 2000h, levando em conta que H está com o valor
35h e L com o valor 8Ch. 
Figura 8.3
Armazenando HL em [2000h].
Esta convenção é utilizada por todos os processadores Intel. Sempre que é
feita uma leitura ou escrita na memória, as partes menos significativas dizem
respeito aos endereço menores, e as partes mais significativas correspondem
aos endereços maiores. 
LDAX e STAX: Essas instruções fazem respectivamente operações de load
(carrega) e store (guarda) do acumulador, usando a posição de memória cujo
endereço está no par BC ou DE. Exemplos:
LDAX D ; A = [DE]
STAX B ; [BC] = A
LDAX B ; A = [BC]
STAX D ; [DE] = A
Note que estamos usando as notações [BC] e [DE] para indicar as posições
de memória cujos endereços são dados por BC e DE. Observe que as
instruções LDAX H e STAX H não existem, mas em seu lugar temos “MOV
A,M” e “MOV M,A” que fazem a mesma coisa. 
Capítulo 8 – Arquitetura de processadores 8-9
XCHG: Trocao valor de HL com o valor de DE. Esta instrução só é usada
na forma:
XCHG ; DE  HL
Trecho de programa com movimentação de dados
Mostraremos agora uma seqüência de instruções de movimentação de dados
apresentadas aqui. Usaremos depois de cada instrução, um ponto-e-vírgula,
seguido de comentários. Esta é uma prática comum nos programas em
assembly e em outras linguagens. O comentário não produz instruções para
o processador, apenas serve para o programador explicar melhor o seu
programa.
INIC: MVI A,100 ; Carrega A com o valor 100 decimal
MOV C,A ; Copia o valor de A para o registrador C
LXI H, 300h ; Carrega HL com o valor 300h. H ficará com 03 e L ficará com 00
MVI M,40 ; Armazena no endereço 300h (apontado por HL), o valor 40
LXI D, 1000h ; Carrega DE com o valor 1000h
MOV A,M ; Move para A o valor armazanedo no endreço 300h
STAX D ; Guarda o valor de A na posição 1000h da memória
SHLD 2000h ; Guarda o valor de HL nas posições 2000 e 2001
XCHG ; Troca os valores de DE e HL
Na listagem acima, “INIC:” é o que chamamos de LABEL. Ele será
entendido pelo assembler como um endereço que deverá ser utilizado
posteriormente em alguma instrução. 
Instruções aritméticas
ADD: Soma com A, o valor do registrador especificado, ou da posição de
memória apontada por HL (M). O resultado da operação é armazenado em
A. Exemplos:
ADD B ; A = A+B
ADD C ; A = A=C
ADD L ; A = A+L
ADD M ; A = A+[HL]
Assim como ocorre com todas as instruções aritméticas e lógicas, os flags (Z,
CY, P, S e AC) são atualizados de acordo com o resultado da operação. Por
exemplo, se somarmos C8h com 72h, o resultado será 13Ah. Este valor não
cabe em 8 bits, portanto o resultado será 3Ah e o bit Carry será ligado para
indicar que ocorreu um “vai 1”. 
8-10 Hardware Total
ADI. Soma com A, o valor constante especificado. O resultado fica
armazenado em A. Exemplos:
ADI 90 ; A = A+90
ADI 35 ; A = A+35
ADC: Semelhante à instrução ADD, exceto que o bit Carry também é
adicionado. Esta operação serve para fazer somas com “vai 1”. Desta foram
podemos dividir números grande em valores de 8 bits, e somar 8 bits de
cada vez. Sempre que fazemos uma soma, o Carry ficará com o “vai 1” da
operação, e assim poderá ser usado para somar a parcela seguinte.
Exemplos:
ADC L ; A = A+L+carry
ADC D ; A = A+D+carry
ADC M ; A = A+[HL]+carry
ACI: Semelhante à instrução ADI, exceto pelo Carry também entrar na
soma. Exemplos:
ACI 90 ; A = A+90+carry
ACI 84 ; A = A+84+carry
SUB: Faz uma subtração de A com o registrador (A = A-registrador) ou com
M. Exemplos:
SUB D ; A = A-D
SUB C ; A = A-C
SUB M ; A = A-[HL]
Nesta operação, o carry é ligado para indicar o resultado negativo, o que
serve para fazer cálculos com vários dígitos, usando o método de “pedir
emprestado”. 
SUI: Subtrai do acumulador, o número especificado. Por exemplo:
SUI 20 ; A = A-20
SUI 82 ; A = A-82
SUI 0DFh ; A = A-DF (hex)
SBB: Similar à instrução SUB, exceto que leva em conta o valor do carry.
Serve para fazer cálculos com o método de “pedir emprestado”. Exemplos:
SBB C ; A = A-C-carry
SBB L ; A = A-L-carry
SBB M ; A = A-[HL]-carry
Capítulo 8 – Arquitetura de processadores 8-11
SBI: Similar à instrução SUI, exceto que leva em conta o valor do carry.
Serve para fazer cálculos com o método de “pedir emprestado”. Exemplos:
SBI 2Fh ; A = A-2Fh-carry 
SBI 73h ; A = A-73h-carry
INR: Incrementa em uma unidade o valor do registrador especificado. Serve
para implementar contadores. Exemplos:
INR A ; A = A+1
INR C ; C = C+1
INR D ; D = D+1
INR L ; L = L+1
INR M ; [HL] = [HL]+1
Quando o valor do registrador é FF e usamos esta instrução, ele passará a
ficar com o valor 00 e o bit Carry será ligado. O bit Zero também será
ligado, indicando que o resultado da operação foi zero. 
DCR: Diminui de uma unidade o conteúdo do registrado especificado. Esta
instrução é usada para implementar contagens decrescentes. Exemplos:
DCR A ; A = A-1
DCR C ; C = C-1
DCR D ; D = D-1
DCR H ; H = H-1
DCR M ; [HL] = [HL]-1
Quando o registrador tem o valor 1 e usamos esta instrução, o seu conteúdo
passará a ser 00. O bit Zero será ligado, indicando que o resultado da
operação foi 0. Se o registrador estiver com o valor 00 e usarmos novamente
esta instrução, seu valor passará a ser FF. O bit Carry será ligado, para
indicar que o resultado tem valor negativo. 
INX e DCX: Essas instruções são similares às instruções INR e DCR, exceto
que operam com pares de registradores (BC, DE e HL) e com o Stack
Pointer. Não podem ser usadas diretamente para implementar contadores,
pois elas não afetam os valores dos flags, ou seja, não “avisam” se o resultado
foi zero, positivo ou negativo. Essas instruções não tinham objetivo de fazer
contagem, mas sim de usar os registradores como ponteiros para a memória.
Ao lançar o 8086, a Intel corrigiu este “deslize”. As instruções
correspondentes nos processadores de 16 bits afetam os valores dos flags, o
que é importante para tomar decisões posteriores em função do resultado da
contagem. Exemplos:
8-12 Hardware Total
INX H ; HL = HL+1
INX D ; DE = DE+1
DCX B ; BC = BC-1
INX SP ; SP = SP+1
Note que apesar do 8080 ser um processador de 8 bits, INX e DCX são
consideradas instruções de 16 bits. 
DAA: Esta instrução é usada na manipulação de números codificados no
formato BCD (Bynary Coded Decimal). Nesta representação, um valor de 8
bits é dividido em 2 grupos de 4 bits. Um grupo de 4 bits representa o dígito
das unidades e o outro grupo representa o dígito das dezenas, no formato
decimal. Números neste formato podem ser somados e subtraídos pelas
mesmas instruções que manipulam números binários. A única diferença é
que no final da operação é preciso usar a instrução DAA para fazer o ajuste
decimal. Por exemplo, se quisermos somar os números 48 e 36 (BCD),
usamos as instruções comuns (ADD, ADI, etc.) e encontraremos o resultado
7E (hex). Entretanto o resultado esperado, considerando o formado BCD,
seria 84 (pensando em decimal). Logo após fazer a soma, se usarmos a
instrução DAA, aquele valor 7E resultará em 84. 
DAD: Esta é uma operação de soma em 16 bits. Soma o valor de 16 bits
presente em HL com o par de registradores especificado. Este “par” pode ser
BC, DE, HL ou SP. O resultado é colocado em HL, e o bit Carry é afetado,
refletindo um eventual “vai 1”. Exemplos:
DAD B ; HL = HL+BC
DAD D ; HL = HL+DE
DAD H ; HL = HL+HL
DAD SP ;HL = HL+SP
Para exemplificar as instruções apresentadas até aqui, vamos mostrar um
exemplo no qual movemos 30 bytes localizados a partir do endereço 1000h
para o endereço 2000h.
LXI H, 1000h ; HL vai apontar para a origem
LXI D, 2000h ; DE vai apontar para o destino
MVI C, 30 ; C será usado como contador:
TRAN: MOV A,M ; Pega o byte da origem
STAD X ; Guarda no destino
INX H ; Aponta para o próximo byte
INX D ; Aponte para o próximo byte
DCR C ; Decrementa o contador
JNZ TRAN ; Vai para TRAN se não chegou a ZERO
Capítulo 8 – Arquitetura de processadores 8-13
Além das instruções já conhecidas, estamos usando a instrução JNZ (Jump if
not Zero). Este instrução é um exemplo de desvio condicional. O programa
continuará a partir do endereço TRAN caso o bit ZERO não esteja ativado,
ou seja, se o resultado da operação anterior (C=C-1) não foi zero. Quando a
contagem chegar a zero, a instrução JNZ não provocará o desvio, e o
programa continuará com a instrução seguinte. 
Instruções lógicas
As instruções lógicas são necessárias para que os programas possam tomar
decisões em função dos dados. São instruções que realizam operações AND,
OR, XOR (ou exclusivo) e NOT (negação). Existem ainda instruções de
comparação, instruções para manipularo bit carry e instruções para rotação
de bits. 
Para entender o funcionamento dessas instruções, temos que lembrar as
tabelas verdade dos operadores NOT, AND, OR e XOR:
X NOT X X Y X AND Y X Y X OR Y
0 1 0 0 0 0 0 0
1 0 0 1 0 0 1 1
1 0 0 1 0 1
1 1 1 1 1 1
Como vemos na tabela acima, o operador NOT faz a inversão do bit sobre o
qual atua. O operador AND dará resultado 1 apenas quando ambos os bits
forem 1, e dará 0 em caso contrário. O operador OR dará resultado 0
somente quando ambos os bits forem 0. O operador XOR dará resultado 1
se os dois bits forem diferentes, e 0 se ambos os bits forem iguais.
X Y X XOR Y
0 0 0
0 1 1
1 0 1
1 1 0
Essas operações são envolvem apenas um bit, mas nas instruções lógicas dos
processadores, atuam individualmente sobre cada um dos bits. Por exemplo,
se calcularmos 10111110 AND 10010101, teremos o seguinte resultado:
10111110
10010101 AND
------------
10010100
8-14 Hardware Total
Note que o primeiro bit do resultado é obtido fazendo a operação AND com
os primeiros bits das duas parcelas, e assim por diante. 
ANA e ANI: Realiza uma operação AND, bit a bit, do acumulador com o
registrador especificado. O resultado da operação fica no acumulador. A
instrução ANI faz o AND do acumulador com um valor constante.
ANA B ; A = A AND B
ANA C ; A = A AND C
ANA A ; A = A AND A
ANA M ; A = A AND [HL]
ANI 3Fh ; A = A AND 3F
Uma das várias aplicações desta instrução é testar se determinados bits são
zero ou 1. Por exemplo, se fizermos ANI 00000100b, podemos usar a seguir
uma instrução JZ ou JNZ que causarão desvio ou não dependendo do fato
do bit 2 estar ligado ou desligado. 
Chegou a hora de apresentar mais um conceito: a identificação dos bits. Em
um grupo de 8 bits, chamamos cada um desses bits, da direita para a
esquerda, de bit 0, bit 1, até bit 7, ou seja:
bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
ORA, ORI, XRA, XRI: ORA faz a operação OR do acumulador com o
registrador especificado; ORI faz o mesmo com um valor constante; XRA faz
a operação XOR (OU Exclusivo) do acumulador com o registrador
especificado, XRI faz o mesmo com um valor constante. Exemplos:
XRA B ; A = A XOR B
XRA C ; A = A XOR C
ORA L ; A = A XOR L
ORI 20h ; A = A OR 20h
XRI 04h ; A = A XOR 04h
XRA A ; A = A XOR A
Aproveitamos para mostrar alguns macetes de programação assembly. A
instrução ORI serve para ligar um bit selecionado. Para ligar os bits 7, 6, 5, 4,
3, 2, 1 e 0 basta fazer um ORI com valores 80h, 40h, 20h, 10h, 8, 4, 2 e 1,
respectivamente. A instrução XRI fará a inversão do bit correspondente (use
os mesmos valores que indicamos para a instrução ORI). A instrução XRA
A tem o efeito de zerar o acumulador. 
CMP, CPI: A instrução CMP compara o acumulador com outros
registradores. A instrução CPI compara o acumulador com um valor
Capítulo 8 – Arquitetura de processadores 8-15
constante de 8 bits. O resultado do acumulador não é afetado. As instruções
apenas afetam os bits Zero e Carry. Após essas instruções podem ser usados
desvios condicionais que testam esses dois bits. Esses bits ficarão ligados ou
desligados de acordo com os valores comparados:
A maior que Valor Z=0 Cy=0
A igual a Valor Z=1 Cy=0
A menor que Valor Z=0 Cy=1
 
Exemplos:
CMP C ; Compara A com C
CMP L ; Compara A com L
CMP M ; Compara A com [HL]
CPI 4Ch ; Compara A com 4C
RLC, RRC: Essas duas instruções são usadas para deslocar os bits do
acumulador. RLC desloca para esquerda e RRC desloca para a direita. A
operação é mostrada na figura 4.
Figura 8.4
Instruções RLC e RRC.
Na instrução RLC, cada bit assume o lugar do bit imediatamente à sua
esquerda (ou seja, o bit imediatamente mais significativo). O bit 7 é
transferido para o bit 0, e uma cópia do bit 7 é feita no Carry. Na instrução
RRC, o deslocamento é feito de forma inversa. O bit 0 é copiado para o bit
7 e para o Carry. Essas instruções têm diversas aplicações, entre as quais, a
implementação de operações de multiplicação e divisão, já que o 8080 não
as possui no seu conjunto de instruções. 
Essas instruções não têm parâmetros. São usadas simplesmente nas formas
RRC e RLC. 
RAL, RAR: Também fazem deslocamentos dos bits do acumulador, para a
esquerda e para a direita. A diferença é que neste caso, a rotação é feita com
8-16 Hardware Total
9 bits, sendo 8 do acumulador e mais o Carry. A operação dessas instruções
é mostrada na figura 5. 
Figura 8.5
Instruções RAL e RAR.
CMA: Complementa o acumulador, ou seja, faz a inversão de todos os seus
bits. 
STC, CMC: Essas instruções servem para carregar valores no Carry. A
instrução STC faz Carry=1, e a instrução CMC inverte o valor do Carry.
Note que não existe uma instrução para zerar o Carry. Ao invés dela,
podemos usar STC seguida de CMC, ou então usar a instrução ANA A ou
ORA A, que não alteram o valor de A mas zeram o Carry. 
Instruções de desvio
As instruções de desvio são importantíssimas, e são executadas o tempo
todo. O processador tende a seguir uma seqüência de instruções, na mesma
ordem na qual são encontradas na memória, ou seja, depois de cada
instrução é executada a instrução seguinte. Um programa que só executa
instruções na sequência não tem muita utilidade. Todos os processadores
precisam de insruções de desvio, que fazem com que a execução seja
continuada a partir de um outro ponto qualquer do programa. Já mostramos
um exemplo de trecho de programa que usa a instrução JNZ (jump if not
zero) para implementar a repetição de um trecho um certo número de vezes.
Um trecho de programa que é executado diversas vezes é chamado de
LOOP. 
JMP: Esta é a principal e mais simples instrução de desvio. É o que
chamamos de desvio incondicional, ou seja, sempre será executada, não
importa em que condições. Por exemplo, ao encontrar a instrução JMP
8000h, o processador continuará a execução do programa a partir das
instruções localizadas no endereço 8000h. 
Capítulo 8 – Arquitetura de processadores 8-17
CALL e RET: A instrução CALL também é um desvio, mas bem diferente
do JMP. É uma chamada de rotina. Uma rotina é um trecho de programa
que pode ser chamado de vários pontos de um programa principal. No final
de uma rotina deve exitir uma instrução RET, que faz o processador retornar
ao ponto imediatamente após a instrução CALL. 
Comparando as instruções CALL e JMP, a única diferença é que no caso da
instrução CALL, o endereço da próxima instrução (endereço de retorno) é
guardado no topo da pilha. O valor do SP (stack pointer é atualizado para
permitir novos empilhamentos). A instrução RET simplesmente obtem o
endereço no topo da pilha e o coloca em PC (Program Counter), fazendo
com que a execução continue de onde parou. 
Mostraremos novamente nosso pequeno trecho de programa que move um
certo número de bytes de uma parte para outra da memória, mas desta vez
usando uma rotina. O trecho começa no endereço 1000, onde carregamos o
par HL com o endereço da origem, DE com o endereço do destino e o
registrador C com o número de bytes (no caso são 16 bytes, que
corresponde a 10 em hexadecimal). A seguir é chamada uma rotina que está
no endereço 1020. Esta é a rotina responsável pela movimentação. Depois
que a rotina é chamada, a próxima instrução a ser executada é JMP 0, que
está no endereço 100A.
1000: 21 00 81 LXI H, 8100
1003: 11 00 82 LXI D, 8200
1006: 0E 10 MVI C,10
1008: CD 20 10 CALL 1020
100A: C3 00 00 JMP 0
...
1020: 7E MOV A,M
1021: 12 STAX D
1022: 23 INX H
1023: 13 INX D
1024: 0D DCR C
1025: C2 20 10 JNZ 1020
1028: C9 RET
Observe que a rotina de movimentação localizada no endereço 1020é
genérica. Ela pode mover dados entre duas posições quaisquer de memória,
dadas por HL e DE. O número de bytes também pode ser qualquer (de 1 a
255), e deve ser dado em C. Dizemos então que HL, DE e C são os
parâmetros desta rotina. 
É importante entender o que acontece com a stack ao executarmos
instruções CALL e RET. Na instrução CALL, o endereço de retorno e
empilhado. Na instrução RET, o endereço de retorno é desempilhado. No
8-18 Hardware Total
trecho de programa mostrado acima, a instrução CALL causará o
empilhamento do endereço 100A, que é o endereço da instrução seguinte, e
será o endereço de retorno. 
Figura 8.6
Empilhamento de um endereço de
retorno na stack, feito por uma instrução
CALL.
A figura 6 ilustra o que está ocorrendo. Digamos que o registrador SP (Stack
Pointer) esteja com o valor inicial 0100. A stack aumenta para trás, ou seja,
para endereços menores. Ao executar a instrução CALL, o processador
empilhará o endrereço 100A nos bytes imediatamente anteriores ao
endereço indicado por SP. Portanto ocupará os endereços 00FF e 00FE. O
SP será atualizado para 00FE, que será o novo topo da pilha. Assim novos
endereços poderão ser empilhados quando forem executadas outras
instruções CALL. 
A instrução RET fará exatamente o inverso do mostrado na figura 6. O Stack
Pointer estará com o valor 00FE, portanto irá obter o endereço de retorno
nas posições 00FE e 00FF da memória, e encontrará 100A. O Stack Poitner
será então atualizado para 0100, que será o novo topo da pilha. 
JMPs, CALLs e RETs condicionais – Além das instruções JMP, CALL e
RET, que são incondicionais, existem suas versões condicionais, que são
executadas apenas quando uma determinada condição é satisfeita. Essas
condições são baseadas nos flags: Zero, Carry, Parity e Signal. São elas:
Instrução Ação Interpretação
JZ Pula se Zero está ligado Pula se o resultado é zero, 
Pula se iguais
JNZ Pula se Zero está desligado Pula se o resultado não é zero, 
Pula se diferentes
JC Pula se Carry está ligado Pula se menor, pula se carry
JNC Pula se Carry está desligado Pula se maior ou igual, pula se não carry
JPE Pula se paridade Par Pula se número de bits 1 é par
JPO Pula se paridade Ímpar Pula se número de bits 1 é ímpar
JP Pula se sinal positivo Pula se resultado positivo ou zero
JM Pula se sinal negativo Pula se resultado negativo
Capítulo 8 – Arquitetura de processadores 8-19
Nesta tabela mostramos a ação de cada uma desas instruções, e ainda uma
interpretação dessas ações. Por exemplo, a instrução JZ pode ser usada logo
depois uma operação aritmética e queremos que seja feito o desvio se o
resultado foi zero. Pode ainda ser usada depois de uma comparação e
queremos que o desvio seja feito se os valores comparados forem iguais. 
Da mesma forma existem as chamadas condicionais de rotinas e os retornos
condicionais:
CZ, CNZ, CC, CNC, CPE, CPO, CP, CM
RZ, RNZ, RC, RNC, RPE, RPO, RP, RM
RST n: Esta instrução é similar a uma instrução CALL. A diferença é que
ela não precisa que seja indicado o endereço, pois está implícito. Podemos
usá-la de 8 formas diferentes:
RST 0 / RST 1 / RST 2 / RST 3 / RST 4 / RST 5 / RST 6 / RST 7
Essas instruções têm o mesmo efeito que:
CALL 0000 / CALL 0008 / CALL 0010 / CALL 0018 / … / CALL 0038
O objetivo dessas instruções é economizar bytes, já que ao invés dos 3 bytes
ocupados por uma instrução CALL, usa apenas um byte. Quando uma certa
rotina é usada muitas vezes em um programa, podemos colocá-la a partir de
um desses endereços e chamá-las através das instruções RST. Note que esses
endereços estão separados apenas por 8 bytes, portanto não é possível
colocar diretamente neles, rotinas maiores que este tamanho. O que
normalmente fazemos é usar nesses endereços, instruções de JMP para outra
área onde ficam as rotinas. 
PCHL: Carrega em PC o valor existente em HL. Isto é equivalente a
executar um JMP para o endereço especificado por HL. É útil quando
queremos desviar para um local variável, em função do valor em HL, obtido
por exemplo, de uma tabela de endereços. 
Operações com a pilha, E/S e controle
Para completar o conjunto de instruções do 8080, falta apenas uma pequena
miscelânea de instruções para para manipulação da stack, entrada/saída e
controle. 
8-20 Hardware Total
PUSH e POP: Já vimos como a stack é usada para emplilhar e desempilhar
endereços nas instruções CALL e RET. São sempre valores de 16 bits. Além
de endereços, podemos ainda empilar e desempilhar dados na stack. Por
exemplo, a instrução PUSH H guardará o valor de HL no topo da stack. A
instrução POP H fará o inverso, ou seja, retirará da pilha o valor do seu topo
e o copiará para HL. As instruções PUSH e POP podem ser usadas com os
parâmetros B, D e H, que operam com os pares BC, DE e HL. Podemos
ainda usar PUSH PSW, que salva em um grupo de 16 bits, o valor do
acumulador e dos flags. A instrução POP PSW faz o inverso. 
XTHL: Lembra da instrução XCHG, que troca os valores de HL e DE? A
instrução XTHL faz algo parecido. Troca os valores de HL e do topo da
pilha. 
SPHL: Já vimos também a instrução “LXI SP, Valor”, que carrega um valor
fixo no stack pointer. Isto é necessário na inicialização dos programas,
quando temos que definir onde ficará a pilha. A instrução SPHL é mais
flexível. Ela cria a stack em qualquer ponto da memória, bastando indicar
seu endereço em HL. 
IN, OUT: São instruções importantíssimas que servem para o processador
trocar dados com o mundo exterior. Através da instrução IN podemos obter
dados provenientes de interfaces que estão ligadas aos periféricos. O dado
lido ficará no acumulador. A instrução OUT faz o inverso, ou seja, transfere
para o endereço de E/S especificado, o valor que está no acumulador.
Exemplos:
IN 70h ; Lê dado que está no endereço de E/S 70h
OUT 40h ; Envia para o endereço de E/S 40h, o valor de A
DI, EI: Essas instruções controlam as interrupções de hardware. DI faz com
que as interrupções sejam desabilitadas. EI faz com que sejam habilitadas
novamente. São úteis durante o atendimento a interrupções de hardware e
em certos trechos críticos que não podem ser interrompidos. 
HLT: Halt. Faz o processador parar de executar instruções. O processador
só sai deste estado quando ocorre uma interrupção. 
NOP: No Oparation. Esta instrução não faz nada. É usada quando queremos
fazer uma pausa entre duas instruções seguidas. Normalmente isso é
necessário quando temos um programa que faz controle direto do hardware.
Capítulo 8 – Arquitetura de processadores 8-21
Isto pode ser necessário, por exemplo, para fazer o processador esperar um
pouco mais pela execução das funções de certos circuitos lentos. 
Um pequeno programa para 8080
Finalizamos a apresentação das instruções do 8080, mostrando um pequeno
programa. Este programa faz o recebimento dos caracteres do teclado e os
coloca na memória a partir do endereço 1000h. O número máximo de
caracteres que poderá ser recebido é 80. Quando terminarmos de digitar a
linha, devemos teclar ENTER, cujo código hexadecimal é 0Dh. Estamos
supondo aqui que o computador tem um console (teclado/vídeo
combinados) ligado em uma interface serial que ocupa os endereços de E/S
80h e 81h. O endereço 80h é a porta de dados, que envia caracteres do para
o vídeo (escrita) e lê caracteres do teclado (leitura). A porta 81h é usada
como status. Seus bits 0 e 1 indicam respectivamente se a interface tem um
dado vindo do teclado e se está pronta para enviar um dado para o vídeo. 
LXI H, 1000h ; Aponta para a área de memória
MVI C,0 ; Zera o contador de bytes
LECH: CALL INCHAR ; Lê caractere do teclado
CPI 0Dh ; Testa se foi ENTER
JZ FIM ;Vai para o fim se teclou ENTER
MOV B,A ; Se não foi enter, guarda caracter em B 
MOV A,C ; Pega o contador de caracteres
CPI 80 ; Testa se chegou a 80
JZ LECH ; Se há chegarm 80, ignora e volta a ler
MOV A,B ; Se não chegou a 80, pega o caracter
MOV M,A ; Guarda caracter na memória
CALL OUTCHAR ; Envia o caracter para o vídeo
INR C ; Incrementa o contador de caracteres
INX H ; Incrementa o ponteiro
JMP LECH ; Vai ler o próximo caracter
FIM: JMP 0 ; Pula para 0000 quando terminar o programa
; Rotina de leitura de caracter
INCHAR: IN 81h ; Lê o status da porta serial
ANI 01 ; Testa se o bit 0 está ligado
JZ INCHAR ; Se está desligado continua tentando
IN 80h ; Lê o código do caracter
RET ; e retorna com o caracter em A
; Rotina que envia para o vídeo, caracter
; que está em A
OUTCHAR: PUSH B ; Salva para BC na pilha
MOV B,A ; Guarda em B o caracter
OUTC1: IN 81h ; Lê o status da porta serial
ANI 02 ; Testa o bit 1
JZ OUTC1 ; Se bit 1 está zerado, continua esperando
MOV A,B ; Pega o caracter
OUT 80h ; Envia o caracter 
POP B ; Restaura o valor original de BC
RET ; e retorna
8-22 Hardware Total
Códigos das instruções do 8080
Apresentamos a seguir uma tabela com os códigos de todas as instruções do
8080. Não que você vá programar 8080, mas para que você tenha uma idéia
da relação entre as instruções e os seus códigos. Na tabela que se segue,
temos as seguintes convenções:
 D8 representa um dado constante de 8 bits
 D16 representa um dado constante de 16 bits
 Addr representa um endereço de 16 bits
Op
Cod
e
Mnemonic
Op
Code Mnemonic
Op
Cod
e
Mnemonic
Op
Cod
e
Mnemonic
Op
Code Mnemonic
Op
Code Mnemonic
00 NOP 2B DCX H 56 MOV D,M 81 ADD C AC XRA H D7 RST 2
01 LXI B,D16 2C INR L 57 MOV D,A 82 ADD D AD XRA L D8 RC
02 STAX B 2D DCR L 58 MOV E,B 83 ADD E AE XRA M D9 -
03 INX B 2E MVI L,D8 59 MOV E,C 84 ADD H AF XRA A DA JC ADDR
04 INR B 2F CMA 5A MOV E,D 85 ADD L B0 ORA B DB IN D8
05 DCR B 30 - 5B MOV E,E 86 ADD M B1 ORA C DC CC ADDR
06 MVI B,D8 31 LXI SP,d16 5C MOV E,H 87 ADD A B2 ORA D DD -
07 RLC 32 STA ADDR 5D MOV E,L 88 ADC B B3 ORA E DE SBI D8
08 - 33 INX SP 5E MOV E,M 89 ADC C B4 ORA H DF RST 3
09 DAD B 34 INR M 5F MOV E,A 8A ADC D B5 ORA L E0 POR
0A LDAX B 35 DCR M 60 MOV H,B 8B ADC E B6 ORA M E1 POP H
0B DCX B 36 MVI M,D8 61 MOV H,C 8C ADC H B7 ORA A E2 JPO ADDR
0C INR C 37 STC 62 MOV H,D 8D ADC L B8 CMP B E3 XTHL
0D DCR C 38 - 63 MOV H,E 8E ADC M B9 CMP C E4 CPO ADDR
0E MVI C,D8 39 DAD SP 64 MOV H,H 8F ADC A BA CMP D E5 PUSH H
0F RRC 3A LDA ADDR 65 MOV H,L 90 SUB B BB CMP E E6 ANI D8
10 - 3B DCX SP 66 MOV H,M 91 SUB C BC CMP H E7 RST 4
11 LXI D,D16 3C INR A 67 MOV H,A 92 SUB D BD CMP L E8 RPE
12 STAX D 3D DCR A 68 MOV L,B 93 SUB E BE CMP M E9 PCHL
13 INX D 3E MVI A,D8 69 MOV L,C 94 SUB H BF CMP A EA JPE ADDR
14 INR D 3F CMC 6A MOV L,D 95 SUB L C0 RNZ EB XCHG
15 DCR D 40 MOV B,B 6B MOV L,E 96 SUB M C1 POP B EC CPE ADDR
16 MVI D,D8 41 MOV B,C 6C MOV L,H 97 SUB A C2 JNZ ADDR ED -
17 RAL 42 MOV B,D 6D MOV L,L 98 SBB B C3 JMP ADDR EE XRI D8
18 - 43 MOV B,E 6E MOV L,M 99 SBB C C4 CNZ ADDR EF RST 5
19 DAD D 44 MOV B,H 6F MOV L,A 9A SBB D C5 PUSH B F0 RP
1A LDAX D 45 MOV B,L 70 MOV M,B 9B SBB E C6 ADI D8 F1 POP PSW
1B DCX D 46 MOV B,M 71 MOV M,C 9C SBB H C7 RST 0 F2 JP ADDR
1C INR E 47 MOV B,A 72 MOV M,D 9D SBB L C8 RZ F3 DI
1D DCR E 48 MOV C,B 73 MOV M,E 9E SBB M C9 RET F4 CP ADDR
1E MVI E,D8 49 MOV C,C 74 MOV M,H 9F SBB A CA JZ ADDR F5 PUSH PSW
1F RAR 4A MOV C,D 75 MOV M,L A0 ANA B CB - F6 ORI D8
20 - 4B MOV C,E 76 HLT A1 ANA C CC CZ ADDR F7 RST 6
21 LXI H,D16 4C MOV C,H 77 MOV M,A A2 ANA D CD CALL Addr F8 RM
22 SHLD ADDR 4D MOV C,L 78 MOV A,B A3 ANA E CE ACI D8 F9 SPHL
23 INX H 4E MOV C,M 79 MOV A,C A4 ANA H CF RST 1 FA JM ADDR
24 INR H 4F MOV C,A 7A MOV A,D A5 ANA L D0 RNC FB EI
25 DCR H 50 MOV D,B 7B MOV A,E A6 ANA M D1 POP D FC CM ADDR
26 MVI H,D8 51 MOV D,C 7C MOV A,H A7 ANA A D2 JNC ADDR FD -
27 DAA 52 MOV D,D 7D MOV A,L A8 XRA B D3 OUT D8 FE CPI D8
Capítulo 8 – Arquitetura de processadores 8-23
28 - 53 MOV D,E 7E MOV A,M A9 XRA C D4 CNC ADDR FF RST 7
29 DAD H 54 MOV D,H 7F MOV A,A AA XRA D D5 PUSH D
2A LHLD ADDR 55 MOV D,L 80 ADD B AB XRA E D6 SUI D8
Observe que alguns códigos, ao serem recebidos pelo processador, não
representam instrução alguma. No caso do 8080, esses códigos são:
08, 10, 18, 20, 28, 30, 38, CB, D9, DD, ED e FD. 
Ao encontrar uma dessas instruções inválidas, o 8080 não fazia nada. Alguns
ciriosos descobriram que certos códigos inválidos eram na verdade instruções
não documentadas da Intel, porém nenhum programador sério ousava
utilizá-las. Como eram instruções não oficiais, não era garantido que fossem
implementadas em todas as versões do processador. No 8085, uma evolução
do 8080, a Intel utilizou duas novas instruções: RIM (20h) e SIM (30h). A
Zilog utilizou esses códigos para usar com as novas instruções do seu
processador Z80. 
Nos processadores modernos, não é permitido executar instruções inválidas.
Quando isso ocorre, o próprio processador gera uma interrupção e indica
operação ilegal. No Windows, isso resulta em uma mensagem como: Erro o
programa xxxx executou uma operação ilegal em .....”
Linguagem Assembly do 8086
Depois desta breve apresentação do assembly do processador 8080, estamos
finalmente entrando na era dos PCs, com o assembly do processador 8086.
Os seus registradores internos são de 16 bits, mas foram inspirados nos
registradores do 8080. Na figura 7, os registradores indicados em branco são
“herdados” do 8080, enquanto os indicados em cinza são novos, próprios do
8086. 
8-24 Hardware Total
Figura 8.7
Registradores internos do
8086.
Os regisradores AX, BC, CX e DX são de 16 bits, mas podem ser tratados
como duas partes de 8 bits. AX é dividido em AH e AL, BX é dividido em
BH e BL, e assim por diante. AX é o acumulador, portanto AL corresponde
ao registrador A do 8080. O registrador BX do 8086 corresponde ao par HL
do 8080 (assim como BH corresponde a H e BL corresponde a L). Da
mesma forma, CX corresponde ao par BC e DX corresponde ao par DE. O
contador de programa (PC) do 8080 é chamado no 8080 de IP (Instruction
Pointer). O Stack Pointer (SP) é similar, e os flags (Cy, Z, AC, P e S) ficam
em um registrador de flags, com 16 bits. 
Esta correspondência entre os registradores do 8086 e do 8080 foi proposital.
Permitiu que programas escritos em assembly do 8080 fossem rapidamente
convertidos para o 8086, mesmo que não da forma mais eficiente. Por
exemplo, as instruções MOV D,B / MOV E,C podiam ser diretamente
traduzidas por MOV DH,CH / MOV DL,CL. Entretanto é muito melhor
usar os recursos de 16 bits, com a instrução MOV DX,CX. Depois de
converter os antigos programas assembly de 8080 para 8086, os produtores
de software passaram a criar programas novos já usando os recursos mais
avançados do 8086, resultando em programas mais eficientes. Programas em
linguagem de alto nível (C, Pascal, etc.) podiam ser convertidos com mais
facilidade, já que eram desvinculados do assembly. 
Novas instruções
Além de ter todas as instruções do 8080 ou instruções similares, o 8086
trouxe novas instruções bem mais avançadas, com execução mais rápida.
Alguns exemplos:
Capítulo 8 – Arquitetura de processadores 8-25
 Multiplicacão de números inteiros de 16 bits
 Divisão de números inteiros de 32 bits
 Rotações com qualquer número de bits
 Movimentação e comparação de strings
 Instruções para manipulação direta de bits
 Instruções de repetição
Registradores BX, BP, SI e DI
Esses registradores permitem várias operações comuns em outros
registradores, e além delas, podem ser usados como índices. Por exemplo,
podemos usá-los para apontar posições de memíria usando expressões como:
[BX+valor] [BX+SI+valor]
[BP+valor] [BX+DI+valor][SI+valor] [BP+SI+valor]
[DI+valor] [BP+DI+valor]
Exemplos:
MOV BX,1000h ; Aponta para o endereço 1000h
MOV AL,[BX+15h] ; Lê para AL o valor que está em 1015h
MOV BX,2000h
MOV SI,100h
MOV AL,[BX+SI+20h] ; Lê para AL o valor que está em 2120h
O uso de índices torna a programação extremamente mais simples quando
temos que lidar com estruturas de dados na memória, como strings e
matrizes. 
Registradores de segmento
O 8086 podia endereçar 1 MB de memória, muito mais que os 64 kB
permitidos pelo 8080. No 8080, toda a memória era tratada como uma única
coleção de bytes, contendo instruções, dados e stack. No 8086, esses
elementos também ficam nesta mesma memória, apesar de maior. Apesar da
memória ser homogênea do ponto de vista físico, seu uso é dividido em
áreas chamados segmentos. Instruções devem ficar no segmento de código,
dados devem ficar no segmento de dados ou no segmento extra, e a stack
deve ficar no segmento de stack. Para manter essas 4 áreas de memória
diferentes, o 8086 possui 4 registradores de segmento, que são:
CS: Code segment
8-26 Hardware Total
DS: Data segment
ES: Extra segment
SS: Stack segment
Cada segmento no 8086 é uma área de memória com 64 kB. Os
registradores de segmento indicam o endereço inicial dos respectivos
segmentos. Note que esses registradores têm 16 bits, enquanto os endereços
de memória do 8086 têm 20 bits. O processador obtém o endereço inicial de
cada segmento adicionando 4 bits zero (ou um dígito 0 hexadecimal) à
direita do valor existente no regitrador de segmento. Por exemplo, se o
registrador CS está com o valor 7BC3, então o segmento de dados começa
no endereço 7BC30.
Figura 8.8
Regitradores de segmento indicam os
inícios dos respectivos segmentos.
A figura 9 mostra como é feito o endereçamento da memória dentro de um
segmento. Todos os acessos a instruções são feitas automaticamente no
segmento de dados. Digamos que CS esteja armazenando o valor 2800h, o
que indica que o segmento de dados começa em 28000h. Digamos que o
registrador IP (Instriction Pointer) esteja com o valor 0153h. Para obter o
endereço de memória, fazemos a seguinte conta: adicionar um zero à direita
do valor do segmento e somar este resultado com o offset, que no caso é o
valor de IP. Encontramos então 28000h+0153h=28153h.
Capítulo 8 – Arquitetura de processadores 8-27
Figura 8.9
Determinação de um endereço absoluto a
partir do segmento e do offset.
Todos os endereços do 8080 são compostos desta forma. O endereço usado
para acessar a memória (de 00000 a FFFFF) é o que chamamos de endereço
absoluto. O endereço absoluto sempre é formado por um valor de segmento
e um offset. O valor do segmento é adicionado de um zero hexadecimal à
sua direita e somado com o valor do offset, resultando no endereço absoluto.
Vejamos um outro exemplo. Digamos que tenhamos DS=8A9Fh e
BX=7CB6h. A instrução MOV AL,[BX] buscará um byte do endereço
absoluto dado por:
8A9F0h +7CB6h = 926A6h 
Observe que cada posição de memória pode ser endereçada de várias outras
formas. Por exemplo, o mesmo endereço absoluto 926A6H pode ser obtido
fazendo DS=9000h e BX=26A6h. 
Todas as instruções a serem executadas são buscadas no segmento de
código, portanto o registrador CS é usado na determinação do endereço
absoluto. Todos os acessos a dados são feitos no segmento de dados,
portanto o processador usa o valor de DS no cálculo do endereço absoluto.
Certas instruções que manipulam strings utilizam o segmento extra (ES é a
base para o cálculo), e as operações com a stack são feitas no segmento de
stack, determinado por SS. 
Usando 4 segmentos de 64kB (código, dados, stack e extra), somos levados a
concluir erradamente que um programa de 8086 pode ter no máximo 64 kB.
Na prática não é isso o que ocorre. Para programas pequenos, não é
necessário usar integralmente os 64 kB de cada segmento, portanto pode
8-28 Hardware Total
ocorrer interseção entre os segmentos. Além disso, instruções especiais
alteram automaticamente o valor de CS em operações de desvio e chamadas
de rotinas, resultando em programas de maior tamanho, podendo até mesmo
usar toda a memória disponível. Um mesmo programa pode ter múltiplos
segmentos de código e de dados, manipulando assim quantidades maiores
de memória. 
Modos de endereçamento
O 8086 possui vários modos de endereçamento:
Imediato: Opera com valores constantes. Exemplos:
MOV AX,0 ; Carrega AX com 0
MOV BX,1000h ; Carrega BX com 1000h
MOV DL,20h ; Carrega DL com 20h
MOV SI,3500h ; Carrega SI com 3500h
Registrador: Quando envolve apenas registradores. Exemplos:
MOV AX,BX ; Copia BX em AX
MOV CX,SI ; Copia SI em CX
MOV DS,AX ; Copia AX em DS
OR BX,CX ; Faz um “OR” de BX com CX. Resultado fica em BX
Direto: Qundo faz referência a um endereço fixo de memória. Exemplos:
MOV AX,[1000h] ; Carrega o valor do endereço 1000h em AL e do endereço 1001h em AH
ADD CX,[2000h] ; Carrega o valor de 16 bits dos endereços 2000h/2001h em CX
CMP SI,[1020h] ; Carrega o valor de 16 bits dos endereços 1020h/1021h em SI
Indexado: Este é o modo de endereçamento mais flexível. Usa os
registradores BX, BP, SI e DI como índices. Os índices podem ser usados
sozinhos ou combinados, ou seja, o valor da soma de BX ou BP com SI ou
DI. Sobre este valor ainda pode ser adicionada uma constante. Exemplos:
MOV CL,[BX]
MOV DL,[BP]
MOV AX,[SI]
MOV AH,[DI]
MOV CX,[BX+5]
MOV DL,[BP+50]
MOV AL,[SI+100]
MOV AX,[DI+1200]
MOV AX,[BX+SI]
MOV CL,[BX+SI+200]
MOV AH,[BP+DI]
MOV DX,[BP+DI+300]
MOV CX,[DI+4800]
Capítulo 8 – Arquitetura de processadores 8-29
MOV DX,[BP+SI]
MOV AH,[BP+SI+2000]
MOV AL,[BP+DI]
MOV DX,[BP+DI+700]
Note que não é permitido usar em uma única instrução, dois
endereçamentos à memória. Por exemplo, não podemos usar MOV [SI],[DI].
Apesar disso podemos mover dados entre quaisquer resitradores e quaisquer
formas de endereçamento da memória (coisa que não era permitida no
8080). No caso do 8086, existem algumas raras exceções. Por exemplo, não
podemos usar livremente os registradores de segmento com todas as
operações que são suportadas pelos demais registradores. Não podemos
usar, por exemplo, ADD DS,AX. Os registradores de segmento permitem
apenas instruções de movimentação de dados. 
Instruções de movimentação de dados
MOV: Move dados entre dois locais quaisquer. Podem ser usados nesta
instrução, qualquer um dos modos de endereçamento já citados. Exemplos:
MOV AX,BX
MOV DI,1000h
MOV [BX+SI],20
MOV CL,19
MOV SI,[BX]
MOV [BP+DI],CX
Note que o 8086 não tem instruções equivalentes a STAX e LDAX do 8080,
que usam pares BC e DE para indexar a memória, já que não existem os
modos de endreçamento [CX] e [DX]. 
XCHG: No 8080 esta instrução permutava os valores de DE e HL. No 8086,
quaisquer valores podem ser permutados, o que engloba todos os
registradores e a memória, endereçada por todos os modos válidos. É
permitido inclusive usá-la com elementos de 8 bits. Exemplos:
XCHG BX,DX
XCHG AX,SI
XCHG AL,BH
XCHG CX,[BX+SI]
XLAT: Esta é uma instrução bastante especializada. É útil para implementar
traduções de códigos. Digamos que tenhamos na memória uma tabela de
256 valores, e queremos obter o valor desta tabela, cujo índice está em AL.
A instrução XLAT faz isso, uma operação equivalente a MOV AL,[BX+AL].
8-30 Hardware Total
Instruções aritméticas
NEG: Inverte o sinal do valor aritmético especificado. Se o número for
positivo, tornar-se-a negativo, e vice-versa. Note que números negativos
necessitam de um bit (o mais significativo) para indicar o sinal, e os demais
para indicar a magnitude. Números com sinal armazenadosem 8 bits podem
portanto variar entre –128 e +127. Com 16 bits, variam entre –32.768 e
+32.767.
NEG AL
NEG AX
NEG BX
NEG DX
NEG byte ptr [BX+SI]
NEG word ptr [DI+4]
Estamos apresentando agora os prefixos byte ptr e word ptr. Esses prefixos
são utilizados para informar ao assembler a real intenção do programador, se
é acessar um dado de 8 ou de 16 bits. Por exemplo, na instrução MOV AL,
[BX], o assembler sabe que o valor da memória a ser acessado é de 8 bits, já
que o outro operando é AL, que é também de 8 bits. Já em instruções como
NEG [BX], o assembler não saberia se a operação deve ser feita sobre o byte
cujo endereço é dado por BX, ou se deve operar sobre os dois bytes (word)
com este endereço. Usamos então os prefixos byte ptr e word ptr quando
necessário para dar esta informação ao assembler. 
ADD, ADC: Soma os dois operandos. O resultado é colocado no primeiro
operando. A operação pode ser feita com 8 ou 16 bits, dependendo do
operando. A instrução ADC soma os dois valores com o bit Carry, o que é
usado para fazer o “vai 1”, agrupando dados de 16 bits para formar dados
com maior número de bits. Exemplos:
ADD BX,SI
ADD AX,[BX+DI]
ADD CL,AH
ADD DX,CX
ADD [SI],DX
ADC CX,[BX+SI]
ADC AH,[BP+SI+3]
ADC DX,BX
ADC [SI],AX
SUB, SBB: Essas duas instruções utilizam os mesmos operandos das
instruções ADD e ADC. Fazem a subtração de valores. A diferença entre
elas é que a SBB subtrai também o valor do bit Carry, tornando possível a
operação de “pedir emprestado”, o que é necessário para agrupar vários
Capítulo 8 – Arquitetura de processadores 8-31
dados de 16 bits, manipulando assim números inteiros com maior número de
bits. Exemplos:
SUB BX,DX
SUB CX,[BP+DI]
SUB CH,DL
SUB CX,AX
SUB [SI],BX
SBB AX,[BX+DI]
SBB CX,[BP+SI+3]
SBB CX,AX
SBB [SI],CX
MUL, IMUL: São as duas instruções de multiplicação do 8086. Ambas
podem operar com 8 e 16 bits. A diferença é que MUL é usada para
números sem sinal, somente positivos, enquanto IMUL aceita números
inteiros, sejam positivos ou negativos. Nesta multiplicação, um dos fatores é
sempre AX ou AL. O outro fator pode ser qualquer operando na memória
ou um outro registrador, com 8 ou 16 bits. Ao multiplicarmos dois valores de
8 bits, o resultado é armazenado nos 16 bits de AX. Ao multiplicarmos dois
valores de 16 bits, o resultado é armazenado em 32 bits, ficando os 16 menos
significativos em AX e os 16 mais significativo em DX. Exemplos:
MUL CL
MUL BX
MUL byte ptr [SI]
IMUL DX
IMUL BX
IMUL CL
DIV, IDIV: São as instruções de divisão. O dividendo pode ser de 16 ou 32
bits. Se for de 16 bits, é usado o valor de AX. Se for de 32 bits, é usado o
valor obtido em DX e AX. O que definirá se o dividendo será de 16 ou 32
bits é o divisor. Se o divisor for de 8 bits, será considerado como dividendo,
AX, o quociente ficará em AL e o resto em AH. Se o divisor for de 16 bits,
será considerado como dividendo o número de 32 bits formado por DX e
AX. O quociente ficará em AX e o resto em DX.
Note que esta instrução parte do princípio de que o resultado “caberá” no
registrador destinado ao quociente. Se não couber, ocorrerá um erro
chamado “estouro de divisão” (divide overflow). Por exemplo, ao fazer a
conta 8000h dividido por 2, usando um divisor de 8 bits, o resultado será
4000h, que não cabe em 8 bits. Para não passar por este problema é melhor
fazer a mesma conta usando o divisor 2 com 16 bits (MOV CX,2 / DIV CX).
Assim como ocorre nas instruções MUL e IMUL, a instrução DIV opera
8-32 Hardware Total
apenas com números inteiros positivos, e a IDIV opera tanto com positivos
quanto com negativos. 
INC, DEC: Incrementa de uma unidade e decrementa de uma unidade. Os
bits Carry e Zero são afetados por essas operações, portanto podem ser
usadas para implementar contadores. Por exemplo, para preencher a tela de
um terminal de vídeo com 2000 caracteres em branco, podemos usar o
seguinte trecho de programa:
MOV DX,2000 ; Número de bytes a serem enviados
ENVIA: MOV AL, 20h ; 20h é o código do caracter “espaço”.
CALL OUTCHAR ; Envia o caracter para o terminal de video
DEC DX ; Decrementa o contador
JNZ ENVIA ; Pula se não chegou a zero
Além de implementar contadores, as instruções INC e DEC também podem
ser usadas para implementar ponteiros para posições de memória, o que e
útil quando queremos manipular dados seqüenciais. 
Instruções lógicas
NOT: Inverte todos os bits do dado especificado. Cada bit 1 se transforma
em 0, e cada bit 0 se transforma em 1. Exemplos:
NEG AX
NEG SI
NEG DL
NEG byte ptr [BX]
NEG word ptr [BP+DI]
NEG byte ptr [1000h]
AND, OR, XOR: São os tradicionais operadores lógicos “E”, “OU” e “OU
Exclusivo”. Não há necessidade de apresentar novamente a tabela verdade
desses operadores, já mostradas quando apresentamos as instruções do 8080.
A diferença aqui é que essas operações podem ser feitas com 8 ou 16 bits, e
os dois operandos podem ser quaisquer, desde que ambos sejam do mesmo
tipo (ou ambos são byte, ou ambos são word). O resultado da operação
ficará armazenado no primeiro operando. 
AND AX,SI
AND CX,[BX+DI]
AND DL,CH
OR [SI],AL
OR AX,1040h
OR byte ptr[SI],20h
XOR BX,DX
XOR [SI+2],AL
XOR AL,AH
Capítulo 8 – Arquitetura de processadores 8-33
Shifts e Rotates
O 8086 tem diversas instruções para deslocar bits para a esquerda e para a
direita. São chamadas de shifts e rotates. As instruções SHL e SHR são
exemplos de shifts. Provocam o deslocamento de todos os bits para a
esquerda e para a direita, respectivamente. Bits 0 são introduzidos à direita e
à esquerda. A operação dessas duas instruções é mostrada na figura 10. Note
que no 8086, qualquer registrador ou posição de memória pode ser usada
com esta instrução. Podemos aplicar um deslocamento de um só bit ou de
múltiplos bits, como mostraremos mais adiante. 
Figura 8.10
Instruções SHL e SHR.
A diferença entre um shift e um rotate é que o shift introduz um bit 0 ou 1
no no bit mais ou no bit menos significativo, como é o caso das instruções
SHL e SHR mostradas na figura 10. Uma instrução de rotate forma uma
espécie de “anel”, na qual o bit que sai em uma extremidade é recolocado
na otura extremidade. A figura 11 mostra as instruções ROL e ROR (rotate
left e rotate right). O exemplo da figura mostra a rotação de valores de 8 bits,
mas também podem ser usados operandos de 16 bits. Note que na instrução
ROL, o bit 7 é realimentado no lugar do bit 0. Na instrução ROR o bit 0 é
realimentado no lugar do bit 7. Em ambas as instruções, o bit que é
realimentado também é copiado no Carry. Este método de rotação é o
mesmo das instruções RLC e RRC do 8080. 
Figura 8.11
Instruções ROL e ROR.
8-34 Hardware Total
As instruções RCL e RCR operam de forma similar, exceto pelo fato do bit
Carry fazer parte da rotação, ao invés de simplesmente ficar com uma cópia
do bit realimentado. A figura 12 mostra o funcionamento dessas duas
instruções, que são exemplos de rotates. Este é o mesmo método de rotação
usado pelas instruções RAL e RAR do 8080. 
Figura 8.12
Instruções RCL e RCR.
Finalmente apresentamos as instruções SAL e SAR, que também são shifts,
da mesma forma como as instruções SHL e SHR já apresentadas. Note que
deslocar os bits uma posição para a esquerda, introduzindo zeros, equivale a
multiplicar o valor por 2, e deslocar os bits uma posição para a direita
equivale a dividir o valor por 2. Isto funciona para números positivos, mas
quando os números podem ter sinal (o sinal é representado pelo bit mais
significativo; 1 significa negativo e 0 significa positivo), é preciso que as
instruções de shift preservem este sinal. Para isso servem as instruções SAL e
SAR, que são chamados shifts aritméticos (assimcomo SHR e SHL são
chamados shifts lógicos). O funcionamento dessas duas instruções é
mostrado na figura 13. 
Figura 8.13
Instruções SAL e SAR.
A instrução SAL é idêntica à instrução SHL, com a introdução de zeros. Já a
instrução SAR tem uma diferença. Ao invés de serem introduzidos zeros no
bit mais significativo, este é realimentado em si próprio, ou seja, é copiado
para o bit seguinte mas o seu próprio valor permanece inalterado. Esta
Capítulo 8 – Arquitetura de processadores 8-35
alteração permite que números negativos continuem negativos ao serem
deslocados para a direita (ou seja, divididos por 2). 
Além da maior variedade de instruções de shifts e rotates, o 8086 permite
operar não apenas com o acumulador, mas com qualquer outro registrador
(exceto registradores de segmento), de 8 ou 16 bits. Também pode operar
com posições de memória, de 8 ou 16 bits. Uma outra diferença importante
é que o deslocamento pode ser feito apenas uma posição (como
exemplificado nas figuras), ou com múltiplas posições. Por exemplo,
podemos deslocar um valor 3 bits para a esquerda, o que equivale a usar 3
vezes consecutivas a mesma instrução. Para aplicar shifts e rotates múltiplos,
basta carregar no registrador CL, o número de vezes que os bits devem ser
deslocados. Exemplos:
SHR AX,1 ; Aplica um shift para a esquerda em AX, de 1 bit.
MOV CL,4 ; Prepara CL com o número de bits a serem deslocados
ROR BX,CL ; Roda BX 4 bits para a direita
SHL DL,1 ; Aplica um shift em DL de 1 posição para a esquerda
Desvios
As instruções de CALL, RET e JMP presentes no 8080 também estão
presentes no 8086. Também temos as formas condicionais da instrução JMP,
mas não temos formas condicionais das instruções CALL e RET. Por
exemplo, não existe a instrução RC (Return if Carry), como no 8080. No seu
lugar temos que fazer uma combinação das instruções JNC e RET. 
As formas condicionais da instrução JMP estão representadas na tabela que
se segue:
8-36 Hardware Total
Note que muitas instruções possuem aliases, ou seja, sinônimos. Por
exemplo, “pule se menor ou igual” é a mesma coisa que “pule se não é
maior”, portanto existem duas instruções idênticas: JBE e JNA (jump if below
or equal / jump if not above). 
Uma outra instrução interessante é LOOP. Esta instrução faz o seguinte:
decrementa o valor de CX, e se este registrador não chegou a zero, faz o
desvio para um label especificado. Por exemplo:
MOV CX,10 ; Contador para 10 vezes
MOV SI,1000 ; SI aponta para endereço 1000 da memória
MOV DI,2000 ; DI aponta para 2000
TRANSF: MOV AL,[SI] ; Pega um byte da origem
MOV [DI],AL ; Guarda no destino
INC SI ; Incrementa ponteiros
INC DI
LOOP TRANSF ; Decrementa CX e se não chegou a zero vai para TRANSF
Neste pequeno trecho de programa as 4 instruções MOV AL,[SI] / MOV
[DI],AL / INC SI / INC DI será executado 10 vezes, que é o valor inicial do
contador CX. Observe que este exemplo é meramente explicativo, já que
existe uma única instrução que faz tudo isso sozinha (REP MOVS), como
veremos mais adiante. O objetivo deste exemplo foi mostrar como a
instrução LOOP pode ser usada para implementar repetições. 
Existem ainda as formas condicionais da instrução LOOP, que são LOOPE e
LOOPNE (ou LOOPZ e LOOPNZ). Essas instruções fazem previamente um
Capítulo 8 – Arquitetura de processadores 8-37
teste no bit Zero, e executação uma instrução LOOP caso a condição seja
satisfeita. Se a condição não for satisfeita, o loop será terminado. Podemos
usar os loops condicionais para fazer uma comparação ou finalizar uma
contagem antes imediatamente antes do final do loop, permitindo assim que
o loop possa ser finalizado mesmo que o contador não tenha chegado a zero.
Existe ainda a instrução JCXZ (jump if CX=0). Como o nome já diz, esta
instrução executa um desvio caso o valor de CX tenha atingido o valor zero.
Note que esta instrução, a instrução LOOP e suas formas condicionais, e as
instruções de shifts e rotates que podem usar em CL o número de bits a
serem deslocados, dão ao registrador CX uma espécie de “personalidade”.
Este registrador é usado como contador em todas essas instruções citadas, e
em outras que ainda vamos apresentar. 
Rotinas e retornos
Como já abordamos, as instruções de chamadas e retornos de rotinas são
CALL e RET, e não possuem formas condicionais. Existem entretanto outras
instruções de chamadas e retornos. 
A instrução INT é uma espécie de “interrupção de software”. Normalmente
é usada para serviços do sistema operacional. Os primeiros 1024 bytes da
memória são reservados para uma área chamada vetor de interrupções. Este
vetor tem 256 elementos, e cada um desses elementos é composto de 4
bytes, sendo 2 para indicar um segmento e 2 para indicar um offset. Cada
um desses 256 elementos é o endereço de uma função do sistema
operacional encarregada de um determinado serviço. Cabe ao produtor do
sistema operacional estipular como essas 256 interrupções serão usadas. Por
exemplo, no MS-DOS, a instrução INT 21h é usada para várias chamadas de
funções básicas de acesso a disco e E/S em geral. 
Quando uma instrução CALL é executada, o endereço de IP é armazenado
na stack. A operação inversa é feita pela instrução RET. Quando uma
instrução INT é executada, os valores de CS e IP são armazenados na stack,
já que serão carregados com novos valores encontrados no vetor de
interrupções. O final de uma rotina de interrupção, seja ela de software ou
de hardware, tem que terminar com uma instrução IRET. A diferença é que
IRET obtém da stack, valores de CS e IP, enquanto uma instrução RET
comum obtém apenas o valor de IP. 
Manipulação da stack
8-38 Hardware Total
As instruções PUSH e POP são utilizadas respectivamente para armazenar e
recuperar valores de 16 bits na pilha. Todos os registradores de 16 bits
podem ser usados com essas instruções, bem como dados de 16 bits da
memória. As instruções PUSHF e POPF são usadas para salvar e recuperar o
registrador de flags. Exemplos:
PUSH BX
PUSH SI
PUSH BP
PUSH DS
POP AX
POP CX
POPF
Interrupções e E/S
Várias instruções são usadas para o processador interagir com o hardware.
As instruções STI e CLI são hadas para habilitar e desabilitar interrupções.
Instruções IN e OUT fazem operações de entrada e saída com 8 ou 16 bits.
Nas instruções de 8 bits é usado o registrador AL, e nas instruções de 16 bits
é usado o registrador AX. Exemplos:
IN AL,80h ; Lê porta de 8 bits no endereço 80h
IN AX,60h ; Lê porta de 16 bits no endereço 60h
OUT 43h,AL ; Envia dado de AL para a porta 43h
OUT 40h,AX ; Envia AL para a porta 40h e AH para a porta 41h
Usadas neste modo, as instruções IN e OUT permitem endereçar portas na
faixa de endereços de 00 a FF. Para endereçar portas em todo o espaço de
endereçamento do processador (0000 a FFFF) é preciso colocar em DX o
endereço da porta a ser acessada. Exemplos:
MOV DX,3F0h ; DX aponta para a porta 3F0
IN AL,DX ; Lê o valor da porta 3F0
MOV DX,278h ; Aponta para a porta 278h
OUT DX,AL ; Envia dado de AL para a porta 278h
Manipulação de strings
O processador 8086 e seus sucessores têm a capacidade de manipular strings,
que são cadeias de caracteres. Essas funções são importantes em
processadores de texto e compiladores. Em todas as instruções de strings, os
registradores SI e DI apontam para as strings envolvidas. SI aponta para a
origem, localizada no segmento de dados. DI aponta para a string destino,
localizada no segmento Extra. Portanto as strings de origem e destino estão
em CS:SI e ES:DI, respectivamente. O registrador CX é usado como
Capítulo 8 – Arquitetura de processadores 8-39
contador, e AL ou AX são usados para manter o dado nas operações de
busca, leitura e escrita.MOVSB e MOVSW – Move um dado (8 ou 16 bits) da origem para o
destino. MOVSB opera com bytes, e é equivalente à seguinte seqüência:
MOV dado8,DS:[SI]
MOV ES:[DI],dado8
INC SI
INC DI
MOVSW opera com dados de 16 bits, e é equivalente à seqüência
MOV dado16,DS:[SI]
MOV ES:[DI],dado16
ADD SI,2
ADD DI,2
Se quisermos que seja movido um certo número de bytes, podemos usar um
contador e decrementá-lo após a instrução MOVSB ou MOVSW, e voltar à
instrução se o contador não chegou a zero. Podemos usar a instrução LOOP
para este fim, que decrementa CX e faz o desvio caso não tenha chegado a
zero. Melhor ainda, podemos usar o prefixo REP antes da instrução. Para
usar este prefixo, carregamos em CX o número de vezes que a instrução
deve ser executada. Usamos então essas instruções nas formas:
REP MOVSB
REP MOVSW
Observe que o prefixo REP faz com a que a instrução seguinte seja
executada CX vezes, mas este prefixo só pode ser usado em operações com
strings. 
Outras operações com strings são:
LODSB e LODSW: Carrega em AL ou AX o dado apontado por DS:SI. O
registrador SI é incrementado de 1 ou 2 unidades dependendo de ser a
operação de 8 ou 16 bits. 
STOSB e STOSW: Armazena AL ou AX em ES:DI. O registrador DI é
incrementado de 1 ou 2 unidades para operações de 8 ou 16 bits,
respectivamente. Aliado ao prefixo REP, essas instruções permitem
preencher uma área de memória com um valor constante. 
8-40 Hardware Total
SCASB e SCASW: Compara AL ou AX com o valor da memória apontado
por ES:DI. O registrador DI é somado com 1 ou 2 para operações de 8 ou
16 bits. O flag Zero é ligado de acordo com o resultado da comparação,
portanto logo após uma instrução SCASB ou SCASW podemos usar um
desvio condicional para indicar se o valor de AL ou AX foi “encontrado” ou
não na memória.
CMPSB e CMPWS: Compara os dados apontados por DS:SI e ES:DI. Os
flags são ligados de acordo com a comparação, portanto podemos usar
depois dessas instruções, um desvio condicional. Os ponteiros SI e DI são
atualizados. Essas instruções permitem achar uma string dentro de uma área
de memória. É o que ocorre quando usamos em um processador de textos, o
comando Localizar. 
Outras instruções
O 8086 tem muitas outras instruções. Optamos por não apresentar todas aqui
para não tornar o capítulo muito complexo. Você pode obter no site da Intel
(www.intel.com), o manual completo da linguagem assembly dos
processadores modernos. A diferença é que existem novas instruções,
sobretudo aquelas para manipular valores de 32 bits. Mesmo não sendo
totalmente completa, esta apresentação resumida atendeu ao nosso objetivo
de mostrar como o processador opera internamente e como os programas
são executados. 
Arquitetura do 80286
O 80286 também é um processador de 16 bits. Possui os mesmos
registradores internos existentes no 8086. Entretanto possui algumas novas
instruções, bem como um novo modo de endereçamento capaz de operar
com 16 MB de memória, o que era uma quantidade espantosa para a época
do seu lançamento (1982), quando a maioria dos computadores tinha 64 kB
de memória. O 80286 podia operar em duas modalidades. O chamado
modo real (8086 real address mode) permite endereçar até 1 MB de
memória. Nesse caso o processador comporta-se como um 8086, apenas
acrescido de algumas novas instruções. Para uso em sistemas operacionais
mais avançados, o 80286 podia operar no modo protegido (protected virtual
address mode). Neste modo, o processador pode operar com 16 MB de
memória física e até 1 GB de memória virtual por tarefa. 
Multitarefa
O 80286 foi criado visando facilitar a multiprogramação ou multitarefa, na
qual vários programas podem ser executados “simultaneamente”. O que
Capítulo 8 – Arquitetura de processadores 8-41
ocorre é uma divisão do tempo entre os vários processos que estão sendo
executados. Uma forma simples de dividir o tempo é alocar períodos iguais
(10 milésismos de segundo, por exemplo), e distrubuir esses períodos entre
os processos. Quando um processo começa a ser executado, será
interrompido 10 ms depois, e o sistema operacional deve fazer com que o
processador dê atenção ao processo seguinte. Desta forma usando um
esquema de “rodízio”, todos os processos são executados ao mesmo tempo,
porém em cada instante um só está efetivamente em execução, e os demais
estão aguardando. O período no qual o processador está dedicado a um
processo é chamado time slice. 
Existem outros esquemas mais sofisticados para implementar a multitarefa.
Processos podem ter prioridades diferentes e time slices diferentes, visando
aumentar a eficiência. Um processo que faz muitas operações de E/S tende a
não utilizar integralmente seu time slice, já que freqüentemente precisa parar
e aguardar pela operação de E/S (a leitura de um arquivo do disco, por
exemplo). Este tipo de processo é classificado como “I/O bounded”. Por
outro lado, processos que fazem poucas operações de E/S e realizam muito
processamento são chamados de “CPU bounded”. Esses processos tendem a
utilizar integralmente o seu time slice. Visando aumentar a eficiênica, o
sistema operacional pode reduzir o time slice e aumentar a prioridade para
os processos “I/O bounded”. Pode ainda aumentar o time slice e reduzir a
prioridade para os processos “CPU bounded”. Enfim, o sistema operacional
pode alterar as prioridades e a duração do time slice para que o trabalho do
processador seja distribuído uniformemente entre os vários processos. 
Novas instruções do 80286
As novas instruções incorporadas a este processador podem, em sua maioria,
ser utilizadas tanto no modo real como no modo protegido. Apenas como
referência rápida, citaremos algumas delas:
PUSHA e POPA: Realizam operações de PUSH e POP com todos os
registradores do processador. Essas instruções tornam mais rápida a
operação de salva e restauração de contexto, necessária nas entradas e saídas
de rotinas e nas mudanças entre uma tarefa e outra. 
IMUL: No 8086 esta instrução fazia a multiplicação de AL ou AX por um
registrador de 8 ou 16 bits. No 80286, esta instrução também pode operar
com valores constantes. Por exemplo, se quisermos multiplicar AX por 38,
basta usar IMUL AX,38. No 8086 era preciso usar algo como MOV CX,38 /
IMUL CX.
8-42 Hardware Total
Shifts e Rotates: No 8086 essas operações eram feitas com 1 bit ou com
múltiplos bits, mas era preciso carregar em CL o número de bits a serem
deslocados. No 80286 o valor pode ser usado diretamente na instrução. Por
exemplo: SHR AX,3
INSB, INSW, OUTSB, OUTSW: São versões mais avançadas das
instruções IN e OUT. Agora essas instruções operam com strings. Portanto é
possível, por exemplo, enviar para um endereço de E/S uma seqüência de
dados da memória. O prefixo REP e o contador CX podem ser usados para
especificar o número de transferências a serem realizadas. 
ENTER e LEAVE: Essas novas instruções são usadas para implementar
rotinas em linguagens de alto nível. A instrução ENTER cria o que
chamamos de stack frame, no qual ficam armazenados parâmetros e
variáveis locais da rotina. A instrução LEAVE realiza o proceso inverso.
Essas duas novas instruções tornaram o uso de rotinas em assembly mais
adequado ao método usado pelas linguagens de alto nível, além de permitir
uma entrada e saída mais rápida das rotinas. 
BOUND: Essa instrução checa se o índice de um array está entre os limites
máximo e mínimo permitidos pelo array. O array nada mais é que um vetor
na memória. Por exemplo, se temos um array A com índices de 0 a 10, seus
elementos são A[0], A[1], A[2], .... , A[10]. Se tentarmos usar uma expressão
como A[30], um programa em linguagem de alto nível deverá ser suspenso

Outros materiais