Você está na página 1de 52

Linguagem Assembly Família Intel

Linguagem ASSEMBLY da Família INTEL

1 Introdução
Este texto apresenta uma descrição da linguagem de montagem do
microprocessador 8086/88. Este processador se tornou um enorme sucesso
comercial, não apenas mas sobretudo devido ao lançamento em 1981 do primeiro
microcomputador pessoal (PC) da IBM, que se baseava no 8088.

Figura 1: Diagrama de blocos da arquitetura interna de um processador 8086/88

Com o sucesso dos PCs e interessada em preservar ao investimento em


software dos seus usuários a Intel, fabricante do processador, preocupou-se em
preservar a compatibilidade das novas gerações de processadores com a
arquitetura básica do 8086/88. Como resultado, mesmo os atuais membros da
família Pentium ainda executam código escrito para os fundadores desta dinastia de
última revisão Ago/2007 1
Linguagem Assembly Família Intel

processadores Intel. Levando ainda em conta a simplicidade da arquitetura do


8086/88 é didaticamente conveniente construir o estudo sobre linguagem de
montagem a partir deste processador.
Este texto apresenta inicialmente a arquitetura interna destes
microprocessadores visível ao programador Assembly. Em seguida é apresentada a
sua linguagem Assembly. O aluno que desejar uma documentação completa,
incluindo as novas instruções da família Pentium e as instruções para dados em
ponto flutuante, poderão fazer o download do Manual correspondente
(http://developer.intel.com/design/pentium4/manuals/245471.htm).

2 Arquitetura interna
A Figura 1 mostra a arquitetura interna dos microprocessadores 8086 e 8088.
Há basicamente duas diferenças entre eles: a largura do barramento externo de
dados (8 bits no 8088, e 16 bits no 8086), e o tamanho do bloco "fila de instruções" (4
bytes no 8088 e 6 bytes no 8086). Os principais blocos são:
a) ULA capaz de executar operações sobre 8 ou 16 bits.
b) Banco de Registradores, constituído de:
• Quatro registradores gerais, de 16 bits, AX, BX, CX e DX, que podem ser
subdivididos em (e referenciados separadamente como) registradores de 8
bits, AH, AL, BH, BL, CH, CL, DH e DL. Neste caso, X representa o
registrador de 16 bits, enquanto H ("high") e L ("low") representam
respectivamente seus 8 bits mais e menos significativos. Os registradores A
têm a função de acumulador para algumas operações lógicas/aritméticas. Os
registradores B são utilizados em algumas instruções como registradores de
base. Há instruções que utilizam implicitamente os registradores C como
contadores. Os registradores D não têm nenhuma função específica, além
de funcionarem como registradores de rascunho.
• Dois Registradores de Índice, de 16 bits, SI e DI.
• Apontador de Pilha, de 16 bits, SP.
• Um segundo Registrador de Base, de 16 bits, BP.
• Contador de Programa, de 16 bits, IP.
• Quatro Registradores de Segmento, de 16 bits, CS, DS, ES, SS. Estes
registradores participam na formação do endereço físico que é gerado nos
pinos de endereço do processador. Sua função será descrita, mais adiante.
• Registrador de Estado, de 16 bits. Na verdade somente 9 destes 16 bits têm
significado, conforme mostra a Figura 2. Cada sinalizador ("flag") tem o
seguinte significado:

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

Figura 2: Registrador de Estado do 8086/8088

última revisão Ago/2007 2


Linguagem Assembly Família Intel

♦ OF - sinalizador de "overflow". Este bit é ativado quando a ALU ao


executar uma operação o transporte para dentro do bit mais
significativo é diferente do transporte para fora do bit mais significativo.
♦ DF - sinalizador de direção. Há instruções que envolvem os
registradores de índice na formação do endereço efetivo dos
operandos. Algumas destas instruções implicam na alteração do
conteúdo do registrador de índice. Dependendo do conteúdo deste
sinalizador, os registradores de índice serão incrementados ou
decrementados automaticamente, quando tais instruções são
executadas.
♦ IF - sinalizador de interrupção. Este sinalizador determina, se o
processador está habilitado ou desabilitado a atender interrupções.
Interrupção será o tema do próximo capítulo.
♦ TF - "trap flag". Quando este sinalizador está ativado, é gerada
internamente uma interrupção a cada instrução executada. A
compreensão de seu funcionamento será postergada para o próximo
capítulo.
♦ SF - sinalizador de sinal. Corresponde ao bit mais significativo do
resultado produzido pela ULA, ao executar operações artiméticas.
♦ ZF - sinalizador de zero. Indica, quando ligado, que o resultado
produzido pela ULA é nulo.
♦ AF - indica o transporte para fora do nibble menos significativo - do bit
3 para o bit 4 - do resultado produzido pela ULA. É utilizado quando se
manipula operandos representados em código BCD ou ASCII.
♦ PF - sinalizador de paridade. Este sinalizador é ativado, quando o
número de 1s nos 8 bits menos significativos do resultado produzido
pela ULA é par, e desativado caso contrário.
♦ CF - sinalizador de transporte.

c) Controle. No esquema apresentado na Figura 1 o controle está dividido em


dois blocos: "Sistema de Controle da EU" e "Lógica de Controle das Barras".
Esta divisão se justifica tendo em vista que o processador está dividido em
duas unidades funcionais que cooperam entre si:
• A Unidade de Execução - "Execution Unit" - (EU): Esta unidade é
responsável exclusivamente pela execução das instruções. Ela não se ocupa
da transferência de informações entre o microprocessador e os demais
componentes do sistema. Quando a execução de uma instrução exige o
acesso a algum operando externo ao microprocessador, a EU gera uma
solicitação à
• A Unidade de Interface com o Barramento - "Bus Interface Unit" - (BIU): Esta
unidade se ocupa exclusivamente com a transferência de informações entre
o microprocessador e o restante do sistema. A forma como estas duas
unidades cooperam será vista adiante.

A Figura 1 contém, além disso, alguns outros elementos. São eles: um


última revisão Ago/2007 3
Linguagem Assembly Família Intel

somador (Σ), a fila de instruções, além dos quatro registradores de segmento já


mencionados. A função destes elementos será discutida mais adiante.

3 Linguagem Assembly do 8086/8088


Nas linhas que se seguem são apresentadas de modo resumido as principais
características da linguagem Assembly do 8086/8088 (ASM86). Para ter uma
descrição detalhada desta linguagem. O aluno deverá recorrer aos manuais e livros
indicados.

3.1 Convenção para o armazenamento de variáveis.


Todos os sistemas reais são capazes de armazenar variáveis que ocupam
mais do que um byte, em posições consecutivas da memória. Assim, se uma
variável ocupa uma palavra de 16 bits, esta variável estará armazenada em dois
endereços consecutivos.
Há aqui duas convenções possíveis:
a) armazenar o byte menos significativo no endereço A+1 e o mais
significativo no endereço A, e
b) armazenar o byte menos significativo no endereço A e o mais significativo
no endereço A+1.
A primeira convenção é chamada de “big endien” e é utilizada pelos
processadores da Motorola, por exemplo. A segunda alternativa é chamada “little
endien” e é utilizada por todos os processadores da linha Intel.
A Figura 3 ilustra as duas convenções para armazenar o valor 3456H.
Deve-se ainda acrescentar que nos processadores Intel o endereço de uma
palavra de dois ou quatro bytes é o endereço do byte menos significativo. Em outras
palavras, se uma variável tem comprimento igual a n bytes, e está armazenadas
nos endereços A,..., A+n-1 , o endereço desta variável será A.

endereço conteúdo endereço conteúdo


A A
A+1 A+1

“little endien” “big endien”

Figura 3: Convenções “little endien” e “big endien”.

3.2 Comandos
Existem dois tipos de comandos na linguagem Assembly-86 (ASM86):
instruções e diretivas.
última revisão Ago/2007 4
Linguagem Assembly Família Intel

As instruções correspondem a códigos binários que são as instruções de


máquina executadas pelo microprocessador. Enquanto as instruções assembler são
simbólicas, as chamadas instruções de máquinas são binárias. A razão para usar
instruções Assembly ao invés de instruções binárias é evidente. É muito mais fácil
para o programador desenvolver de dar manutenção num programa escrito na
forma de símbolos que sugerem a função de cada instrução do que num programa
constituído por uma cadeia de 0’s e 1’s. O papel do programa montador (Assembler)
é traduzir as instruções Assemby para as instruções de máquina.
Ao contrário do que ocorre com as instruções, o montador não gera para as
diretivas nenhuma instrução de máquina. As diretivas são informações fornecidas
pelo programador que auxiliam o montador no processo de montagem.
As instruções podem ter até cinco campos:

((rótulo :) (prefixo) mnemônico (operando(s)) (;comentários))


onde os parênteses denotam que se trata de campos opcionais (os parênteses não
são escritos pelo programador).
O campo rótulo fornece um nome à posição de memória que contém a
instrução, de tal maneira que se pode fazer referência a ela simbolicamente numa
instrução de desvio (p. ex.: JMP) em algum outro ponto do programa.
Um prefixo leva o montador a gerar um byte de prefixo que modifica de
alguma forma a execução normal da instrução. O uso de tais prefixos será melhor
esclarecido mais adiante neste texto.
O mnemônico identifica o tipo de instrução (p. e. MOV para movimentação,
ADD para adição, etc.) que deve ser gerada..
Uma instrução pode ter zero, um ou dois operandos separados por uma
vírgula.
Os comentários não afetam a execução de um programa, mas constituem
meramente um recurso que facilita a compreensão da lógica implementada no
programa. É muitíssimo útil durante o desenvolvimento ou posteriormente na
manutenção do programa. Recomenda-se enfaticamente que o aluno utilize
extensivamente comentários em seus programas.
As diretivas podem ter até quatro campos:

(nome) mnemônico (operando(s)) (;comentários)


Algumas diretivas exigem um nome, enquanto outras proíbem um nome. O
montador reconhece a diretiva a partir do mnemônico escrito no segundo
campo.Todos os eventuais operandos são escritos em seguida.

3.2.1 Algumas Diretivas

A relação de diretivas apresentada nesta seção é apenas adequada aos


objetivos deste curso sem ser completa. Outras diretivas serão apresentadas nas
última revisão Ago/2007 5
Linguagem Assembly Família Intel

aulas de laboratório.

3.2.1.1 Constantes

Constantes numéricas podem ser apresentadas em binário, decimal, octal e


hexadecimal. A base utilizada deve ser indicada pelo programador colocando à
esquerda do numeral, respectivamente as letras B, D, Q e H. Quando nenhuma
letra for indicada o montador assume que se trata da base decimal.
Muitos montadores exigem que o primeiro dígito de um número em base
hexadecimal seja um numeral. Na prática isso significa que os números
hexadecimais que iniciam com A, B, C, D, E ou F, devem ser digitados tendo um 0
(zero) na frente.
Todas as constantes devem ser inteiras representáveis em 16 bits, incluindo
sinal. Números negativos são representados em complemento de dois.
Caracteres são apresentados entre apóstrofes e podem aparecer em cadeias
de até 255 caracteres, quando utilizadas para iniciar posições de memória. Quando
utilizadas como operandos imediatos, caracteres podem ter no máximo dois bytes.
A diretiva EQU é utilizada para associar um símbolo a uma constante.
O uso de constantes é ilustrado a seguir.

MOV STRING[SI],’A ; caracter

MOV STRING[SI], 41h ; equivalente em hexadecimal

ADD AX, 0C4H ; constantes hexa iniciam com numeral

OCTAL_8 EQU 10Q ; 8 em octal

OCTAL_9 EQU 11Q ; binário

UMS EQU 11111111B ; 9 em octal

MENOS_5 EQU -5 ; decimal (default)


MENOS_8 EQU -6D
; ‘D’ indica que é decimal

3.2.1.2 Definição de Dados


A maioria dos programas iniciam difinindo as variáveis com que eles vão
trabalhar. Três diretivas, DB, DW e DD são usadas para alocar e dar nome a
posições de memória. São usadas para definir variáveis em três unidades: DB
significa “define byte”, DW significa “define word” e DD significa “define doubleword”.
última revisão Ago/2007 6
Linguagem Assembly Família Intel

Os operandos destas diretivas indicam ao montador quantas unidades de


armazenamento alocar - respectivamente 1, 2 e 4 bytes - e qual o valor inicial, se
houver. Os exemplos abaixo esclarecem como utilizar estas diretivas:
ALFA DB ? ; não inicializada
BETA DW ? ; não inicializada

GAMA DD ? ; não inicializada

UPSILON DW 5 ; constante 05H

IOTA DB ‘HELLO’ ; contém 48 45 4C 4C 4F H

MU DD 100 DUP 0 ; aloca e inicializa 100 bytes com 0 (zero)

Para cada variável num programa ASM86, o montador registra a informação


do tipo, ou seja, do número de bytes alocados a ela. Quando uma variável é
referenciada no programa, o montador utiliza esta informação para determinar a
forma da instrução de máquina que deve ser gerada.
Se o uso da variável conflita com o seu atributo, o montador gera uma
mensagem de erro. Há casos em que o programador deve indicar explicitamente ao
montador qual é o tipo do operando. Por exemplo, a instrução:
MOV [BX],5
escreve o valor 5 na posição de memória, cujo endereço está definido pelo
conteúdo do registrador BX. Esta instrução não identifica o tipo da variável, se é um
byte, uma palavra ou uma palavra dupla. Note, que dependendo do tipo da variável,
a execução desta instrução envolverá a modificação de 1, 2 ou 4 bytes da memória.
A informação do tipo de variável pode ser fornecido através das diretivas: byte ptr,
word ptr, dword ptr, como nos exemplos a seguir
MOV word ptr [BX],5 ; indica que é palavra

SUB byte ptr [BX][SI],1 ; indica que é byte

ANA BETA,32H ; implícito que é palavra

CMP byte ptr ALFA[SI], 10 ; indica que é byte (redundante)

DEC byte ptr GAMA ; indica que é byte apesar da definição

A primeira e segunda linhas do exemplo esclarecem que o primeiro operando


da instrução é um byte. Na terceira linha não é necessário utilizar estas diretivas,
pois o montador assume o tipo indicado quando da definição da variável, neste
caso, uma palavra (vide exemplo anterior). Na quarta linha o uso da diretiva é
redundante e, portanto desnecessário, por que a variável já havia sido definida (vide
última revisão Ago/2007 7
Linguagem Assembly Família Intel

exemplo anterior), como byte. A última linha tem um aspecto interessante. Embora a
variável GAMA tenha sido definida como palavra dupla, a instrução decrementa
apenas o byte apontado pelo endereço GAMA, sem alterar os demais 3 bytes da
palavra dupla.
A diretiva TYPE identifica a unidade de alocação de memória (1 = byte; 2 =
palavra, 4 = palavra dupla).
A linguagem provê duas diretivas: LENGHT e SIZE, que podem ser escrito
nas instruções com informações de atributo. LENGHT faz o montador retornar com
o número de unidades de armazenamento (bytes, palavras ou palavras duplas)
ocupadas por um vetor. SIZE leva o montador a retornar o número de bytes
ocupados pela variável ou vetor.
Estas diretivas permitem que se escrevam seqüências de instruções que não
precisam ser mudadas (somente montadas novamente), se os atributos das
variáveis mudarem, por exemplo, um vetor de bytes muda para um vetor de
palavras.
O exemplo a seguir ilustra uma aplicação típica destas diretivas.

; soma do conteúdo de uma tabela em AX

; observe que a mesma instrução funciona para

; TABELA DB 50 DUP(?)

; TABELA DD 50 DUP(?)

SUB AX,AX ; zera contador

MOV CX, LENGHT TABELA ; número de iterações

MOV SI, SIZE TABELA ; aponta p/ o fim do vetor

SOMA_OUTRO:

SUB SI, TYPE TABELA ; pega elemento anterior

ADD AX, TABELA [SI] ; soma um elemento

; AX contém a soma

Existe também a diretiva OFFSET. Basta por enquanto considerar que esta
diretiva retorna o próprio endereço da variável referenciada no programa logo em
seguida a esta diretiva.
Do mesmo modo, algumas outras diretivas relacionadas com o mecanismo de
segmentação do processador, não serão apresentadas aqui. Embora todo programa
última revisão Ago/2007 8
Linguagem Assembly Família Intel

deva contê-las para que o montador funcione corretamente, este texto limita-se a
mencioná-las sem discutir em detalhes o seu papel.

3.2.1.3 Início e Final de Subrotinas


Em ASM86 uma subrotina (rotina) é ativada através da instrução CALL, como
se verá adiante. O código da subrotina termina necessariamente com a instrução
RET, que desvia o fluxo controle para a instrução seguinte ao CALL. As instruções
de uma rotina são emolduradas pelas diretivas PROC e ENDP, como no exemplo a
seguir:
HISTOGRAMA PROC FAR
; Esta rotina assume que o parâmetro de entrada está em AL,
; e incrementa um contador numa tabela de frequência,
; baseada no valor do parâmetro de entrada.
XOR AH,AH ; zera o byte mais significativo de AX
MOV SI,AX ; índice para a tabela
INC FREQ[SI] ; incrementa o contador
RET
HISTOGRAMA ENDP
O uso da diretiva NEAR/FAR será esclarecido posteriormente.

3.3 Modos de Endereçamento


O processador 8086 dispõe de várias formas de referenciar os operandos das
instruções. Os operandos podem estar em registradores, dentro da própria
instrução, na memória ou em portas de E/S. Além disso, o endereço de memória e
de portas de E/S, podem ser calculados de várias maneiras. Estes modos de
endereçamento conferem flexibilidade ao conjunto de instruções do processador.
Esta seção apresenta uma breve descrição dos modos de endereçamento
disponíveis neste processador.

3.3.1 Registrador
Neste modo de endereçamento, o operando está contido num dos
registradores internos da máquina. Estas instruções são muito compactas pois o
“endereço” do registrador está codificado em “uns poucos bits”. O acesso a este
operando é realizado inteiramente dentro do próprio processador, e não envolve
acesso externo à memória. Exemplos:

MOV AL,BL
SUB CX,DX
DEC CL

última revisão Ago/2007 9


Linguagem Assembly Família Intel

3.3.2 Imediato
O operando está contido na própria instrução. Este operando pode ter 8 ou 16
bits. Exemplos:
SUB CL,2
ADD DX,MENOS_5
Note nos exemplos que as instruções utilizam também o modo de
endereçamento registrador. De um modo geral, uma mesma instrução pode usar
modos de endereçamento diferentes para cada um dos seus operandos. No caso
deste processador cada instrução pode ter no máximo dois operandos. Cada
operando pode ser referenciado usando um modo de endereçamento distinto.
Quando o operando está na memória, o processador calcula o endereço da
posição correspondente das formas que serão explicitadas a seguir. Este endereço
é chamado de endereço efetivo (effective address - EA).
Na realidade o endereço efetivo não é propriamente o endereço do operando,
mas a distância entre o endereço da posição de memória e um certo endereço de
referência chamado endereço de início de segmento. Por enquanto convém admitir
que o endereço efetivo é o próprio endereço do operando. Maiores esclarecimentos
serão dados em seções posteriores.1

opcode mod reg r/m desloc camento

Endereço
Efetivo

Figura 4: Ilustração do modo de endereçamento direto

Nos modos de endereçamento que serão apresentados a seguir, o endereço


efetivo será dado pela soma de até três componentes: base (fornecida por um dos
registradores de base - BX ou BP), índice (fornecido pelo conteúdo de um dos
registradores de índice - SI ou DI) e um deslocamento (contido na própria instrução).
O que caracteriza cada um dos modos de endereçamento a seguir é qual ou quais
destes três componentes estarão presentes na soma.

1
Na realidade o endereço efetivo não é propriamente o endereço do operando, mas a distância
deste a um certo endereço de referência, chamado endereço de início de segmento.
última revisão Ago/2007 10
Linguagem Assembly Família Intel

3.3.3 Direto
Trata-se do modo de endereçamento de memória mais simples. Não envolve
nenhum registrador. O endereço efetivo é tomado diretamente do deslocamento
contido na própria instrução. Este modo de endereçamento é tipicamente utilizado
para referenciar escalares. A Figura 4 ilustra o formato destas instruções.
Alguns exemplos:
MOV AX,[8000H]
SUB CX,BETA
DEC byte ptr GAMA
3.3.4 Registrador Indireto
O endereço do operando pode ser tirado de um dos registradores de base
(BX ou BP) ou índice (SI ou DI), como mostra a Figura 5. Qual destes registradores
é usado está definido num dos campos da instrução.

opcode mod reg r/m

BX ou BP
ou Endereço
SI ou DI Efetivo

Figura 5: Ilustração do modo de endereçamento registrador indireto.

Somente um registrador de base ou de índice pode estar presente neste


modo de endereçamento. Os registradores de 8 bits, assim como AX, DX, CX, SP,
além dos registradores de segmento (CS, DS, ES, SS) são proibidos aqui, a menos
de raras exceções. Exemplos:
MOV AL,[BX]
SUB [SI],DX
Convém, contudo, notar que qualquer registrador de uso geral pode ser
utilizado neste modo de endereçamento nas instruções do tipo JMP e CALL.
Exemplos destes casos são:

JMP [AX]
CALL [DX]

última revisão Ago/2007 11


Linguagem Assembly Família Intel

3.3.5 Relativo a uma Base


Neste modo de endereçamento, o endereço efetivo é dado pela soma de um
deslocamento (definido na própria instrução) e pelo conteúdo de um registrador de
base, mais especificamente, do registrador BX ou do registrador BP.

opcode mod reg r/m desloc camento

BX ou BP
+
Figura 6: Ilustração do modo de endereçamento com base.
Endereço
Efetivo

Este modo de endereçamento é freqüentemente utilizado para


referenciar estruturas que podem estar armazenadas em endereços diferentes na
memória. Antes da instrução que referencia o dado propriamente dita normalmente
se introduzem instruções que carregam no registrador de base o endereço inicial da
estrutura. O elemento da estrutura é indicado pelo deslocamento. Cópias diferentes
de uma mesma estrutura podem ser referenciadas simplesmente mudando o
conteúdo do registrador de base.
Abaixo são dados alguns exemplos deste modo de endereçamento.
MOV AL,[BX+0B000H]
SUB CL,MU[BX]
ADD dword ptr [BP][1000H], 43H
3.3.6 Indexado
No modo de endereçamento indexado o endereço efetivo é calculado a partir
da soma de um deslocamento mais o conteúdo de um registrador de índice (SI ou
DI, conforme definido no campo mod_reg_r/m), como mostra a Figura 7. Este modo
de endereçamento é freqüentemente utilizado para referenciar elementos de um
vetor: o deslocamento apontando para o início do vetor e o valor do registrador de
índice selecionando um dos seus elementos. Como todos os elementos de um vetor
têm o mesmo comprimento, uma aritmética simples com o registrador de índice
selecionará qualquer elemento.

última revisão Ago/2007 12


Linguagem Assembly Família Intel

opcode mod reg r/m desloc camento

SI ou DI
+ Endereço
Efetivo

Figura 7: Ilustração do modo de endereçamento Indexado.

Abaixo são dados alguns exemplos deste modo de endereçamento.


MOV AL,[SI+0B000H]
SUB CL,MU[SI]
ADD word ptr [DI][1000H], 43H

3.3.7 Indexado com Base


O modo de endereçamento indexado com base gera o endereço efetivo
somando o conteúdo de um registrador de base (BP ou BX), o conteúdo de um
registrador de índice (SI ou DI) e um deslocamento, como mostra a figura 8. É
importante salientar aqui que neste modo de endereçamento está presente um
registrador de base e um registrador de índice. Qualquer outra combinação de
registradores é inválida. Este modo é freqüentemente utilizado para referenciar
vetores localizados na pilha, como será discutido adiante. Novamente neste caso o
campo mod_reg_r/m da instrução define quais os registradores participam do
cálculo do endereço efetivo.
Abaixo são dados alguns exemplos deste modo de endereçamento.
MOV AL,[BX+SI+ 5H]
ADD [BP][DI][1000H], AX

última revisão Ago/2007 13


Linguagem Assembly Família Intel

opcode mod reg r/m desloc camento

BX ou BP

+ Endereço
Efet-ivo

SI ou DI

Figura 8: Ilustração do modo de endereçamento indexado com base.

3.4 Conjunto de Instruções do 8086/8088


A apresentação que se segue não contém todos os detalhes de cada instrução.
Ela se propõe a ser um esquema a ser seguido em aula e ao mesmo tempo um
resumo do assunto.
É importante ter em mente que todas estas instruções estão presentes no
conjunto de instruções dos demais processadores da família 80X86, de tal sorte que o
conhecimento aqui adquirido pode ser utilizado na programação de sistemas mais
modernos.

3.4.1 Convenções
Na apresentação a seguir adota-se a seguinte convenção:
d - uma quantidade de 8 ou 16 bits.
d8 - uma quantidade de 8 bits.
d16 - uma quantidade de 16 bits.
r, r1, r2 - um registrador de 8 ou 16 bits de uso geral.
r8 - um registrador de 8 bits.
r16 - um registrador de 16 bits.
() - o conteúdo de um registrador, ou posição de memória,
dependendo do que estiver entre os parênteses.
m - endereço de um byte ou uma palavra na memória
última revisão Ago/2007 14
Linguagem Assembly Família Intel

m8 - endereço de um byte na memória


m16 - endereço de uma palavra de 16 bits.
m32 - endereço de uma palavra dupla de 32 bits.
AX/AL - o registrador AX ou AL, dependendo do operando envolvido ser
de 16 ou 8 bits.
alvo - a definição de um endereço na área de código da memória.
flags - os 16 bits do registrador de estado.
r1:r2 - indica um valor formado pela composição do conteúdo de r1 e r2.
Esta composição pode se dar pela concatenação dos dois valores
(p. ex. DX:AX) ou pelo modo próprio do microprocessador formar
o endereço (p. ex. CS:DX).

As convenções utilizadas na descrição que se segue, acabará se tornando


clara na medida em que as instruções forem sendo apresentadas. Para cada tipo de
instrução aparece do lado esquerdo uma descrição sintática e uma breve descrição
semântica da instrução. Ao centro, são dados alguns exemplos, e do lado esquerdo o
número de ciclos de relógio consumidos na execução da instrução.

3.4.2 Instruções de Transferência de Dados


MOV r1,r2 MOV AL,CH 2
MOV CX,BP
MOV r,m MOV AX,TEMP_RESULT 10/8+EA
MOV CL,ARRAY[SI],
MOV m,r MOV COUNT[DI],CX 10/9+EA
MOV [SI+1000],DX
MOV r,d MOV AX,99 4 4
MOV SI,61CH
MOV m,d MOV byte ptr [SI], 0FH 10+EA
MOV word ptr [BX+1234],57
XCHG r1,r2 XCHG AX,BX 3/4
XCHG CL,CH
XCHG m,r XCHG SEMAFORO,AX 17+EA
XCHG AL,[BX+2]
XLAT XLAT 11
(AL) ← ((BX+AL))

última revisão Ago/2007 15


Linguagem Assembly Família Intel

LAHF LAHF 4
(AH7,6,4,2,0)← (SF,ZF,AF,PF,CF)

SAHF SAHF 4
(SF,ZF,AF,PF,CF) ←(AH7,6,4,2,0)

3.4.3 Instruções de Manipulação de Cadeias de Caracteres (string)


Cada uma das instruções deste grupo tem três formatos diferentes. Para o primeiro
formato (terminado com um "S"), o montador entende que o string é de bytes ou de
palavras. O segundo (terminado com um "SB") e terceiro (terminado com um "SW")
indicam explicitamente que se trata respectivamente de um string de bytes e palavras.

MOVS/MOVSB/MOVSW MOVSB 18
MOVSW

CMPS/CMPSB/CMPSW CMPSB 22
CMPSW

SCAS/SCASB/SCASW SCASB 15
SCASW

LODS/LODSB/LODSW LODSB 12
LODSW

STOS/STOSB/STOSW STOSB 11
STOSW

REP REP MOVSB 9+17*rep


REP REP LODSW 9+13*rep
REP REP STOSW 9+10*rep
REPZ/REPE REPZ CMPS 9+22*rep
REPNZ/REPNE REPNZ SCAS 9+15*rep
Nas cinco últimas linhas da tabela contém prefixos usados para repetir a
última revisão Ago/2007 16
Linguagem Assembly Família Intel

execução da instrução de manipulação de string escrita em seguida pelo número de


vezes especificado em CX. Diante das instruções MOVS/MOVSB/MOVSW,
LODS/LODSB/LODSW e STOS/STOSB/STOSW, pode-se usar o prefixo REP, que
decrementa o registrador CX, sem afetar os flags, e repete a instrução de string,
enquanto CX ≠ 0. Diante das instruções CMPS e SCAS, pode-se usar os prefixos
REPZ(ou REPE) ou REPNZ(ou REPNE). REPZ decrementa CX, sem afetar os flags,
e repete a instrução de string, enquanto CX ≠ 0 e ZF = 1. O prefixo REPNZ
decrementa CX, sem afetar os flags, e provoca a repetição da instrução de string,
enquanto CX ≠ 0 e ZF = 0.

3.4.4 Instruções de Manipulação de Endereços


LEA r16,m16 LEA BX,[BP][DI] 2+EA
(r16) ← m16
LDS r16,m32 LDS SI,DATA_SEG[DI] 16+EA
(r16) ← (m32)
(DS) ← (m32)+2
LES r16,m32 LES SI,DATA_SEG[DI] 16+EA
(r16) ← (m32)
(ES) ← (m32)+2

3.4.5 Instruções Aritméticas

3.4.5.1 Adição
ADD r1,r2 ADD AX,SI 3
ADD CL,DH
ADD r,m ADD DL,[300] 9+EA
ADD CX,[BX+1234H]
ADD m,r ADD byte ptr [300],DL 16+EA
ADD word ptr [SI+BX+2],AX
ADD r,d ADD AL,4 4
(r) ← (r) + d ADD AX,50
ADD m,d ADD byte ptr [DI],54 17+EA
(m) ← (m) + d ADD word ptr [BX+4],4FFH

última revisão Ago/2007 17


Linguagem Assembly Família Intel

ADC r1,r2 ADC BX,DX 3


ADC DH,AL
ADC r,m ADC BL,[456] 9+EA
(r) ←(r) + (m) + (CF) ADC CX,word ptr [300H]
ADC m,r ADC byte ptr [300],DL 16+EA
(m) ←(m) + (r) + (CF) ADC word ptr [SI],BX
ADC r,d ADC CL,24 4
(r) ← (r) + d + (CF) ADC BX,50
ADC m,d ADC byte ptr [DI],54 17+EA
(m) ← (m) + d + (CF) ADC word ptr [BX+4],4FFH
INC r INC AX 2
(r) ←(r) + 1 INC CL 3
INC m 15+EA
(m) ←(m) + 1 INC word ptr [BX+100H]

3.4.5.2 Subtração
SUB r1,r2 SUB AX,SI 3
SUB CL,DH
SUB r,m SUB DL,[300] 9+EA
(r) ← (r) - (m) SUB CX,[BX+1234H]
SUB m,r SUB byte ptr [300],DL 16+EA
(m) ← (m) - (r) SUB word ptr [SI+BX+2],AX
SUB r,d SUB AL,4 4 4
(r) ← (r) - d SUB AX,50
SUB m,d SUB byte ptr [DI],54 17+EA
(m) ← (m) - d SUB word ptr [BX+4],4FFH
SBB r1,r2 SBB BX,DX 3
(r1) ←(r1) - (r2) - (CF) SBB DH,AL

última revisão Ago/2007 18


Linguagem Assembly Família Intel

SBB r,m SBB BL,[456] 9+EA


SBB CX,word ptr [300H]
SBB m,r SBB byte ptr [300],DL 16+EA
(m) ←(m) - (r) - (CF) SBB word ptr [SI],BX
SBB r,d SBB CL,24 4
(r) ←(r) - d - (CF) SBB BX,50
SBB m,d SBB byte ptr [DI],54 17+EA
(m) ← (m) - d - (CF) SBB word ptr [BX+4],4FFH
DEC r DEC AX 2
(r) ←(r) - 1 DEC Cl 3
DEC m DEC byte ptr [SI] 15+EA
DEC word ptr [BX+100H]
NEG r NEG BX 3
NEG AL
NEG m NEG byte ptr [BX+DI] 16+EA
(m) ←0 - (m) NEG MULTIPLICADOR
CMP r1,r2 CMP BX,CX 3
CMP AL.BL
CMP r,m CMP DH, byte ptr ALPHA 9+EA
(r) - (m) CMP CH,[SI+BX+10]
CMP m,r CMP [BP+2],SI 9+EA
(m) - (r) CMP [SI],AX
CMP r,d CMP BL,02H 4
(r) - d CMP DL,85H
CMP m,d CMP RADAR[DI],3420H 10+EA
(m) - d

última revisão Ago/2007 19


Linguagem Assembly Família Intel

3.4.5.3 Multiplicação
MUL r MUL BL 70-77
MUL CX 118-133

MUL m MUL byte ptr MES[SI] (76-83)+EA

(AX) ← (AL) * (m8) MUL word ptr DIA[SI] (124-139)+EA

(DX:AX) ← (AX) * (m16) MUL BAUD_RATE


(sem sinal)
IMUL r IMUL BL 80-98
(AX) ← (AL) * (r8) IMUL CX 128-154
(DX:AX) ← (AX) * r16)
(com sinal)
IMUL m IMUL byte ptr MES[SI] (86-104)+EA

(AX) ← (AL) * (m8) IMUL word ptr DIA[SI] (134-160)+EA

(DX:AX) ← (AX) * (m16)


(com sinal)

3.4.5.4 Divisão
DIV r8 DIV CL 80-90

DIV r16 DIV DX 144-162

(sem sinal)

última revisão Ago/2007 20


Linguagem Assembly Família Intel

DIV m8 DIV byte ptr [SI] (88-96)+EA

(sem sinal)
DIV m16 DIV word ptr [BX] (150-168)+EA

(AX)←(DX:AX)/(m16)
(DX)←(DX:AX)mod(m16)
(sem sinal)
IDIV r8 IDIV CL 101-112
(AL)←(AX)/(r8)
(AH)←(AX) mod (r8)
(com sinal)
IDIV r16 IDIV DX 165-184
(AX)←(DX:AX)/(r16)
(DX)←(DX:AX) mod (r16)
(com sinal)
IDIV m8 IDIV byte ptr [SI] (107-118)+EA

(AL)←(AX)/(m8)
(AH)←(AX) mod (m8)
(com sinal)
IDIV m16 IDIV word ptr [BX] (171-190)+EA

(AX)←(DX:AX)/(m16)
(DX)←(DX:AX)mod(m16)
(com sinal)
CBW CBW 2
Se (AL)<0,

então (AH) ←0FF

caso contrário, (AH) ←00

última revisão Ago/2007 21


Linguagem Assembly Família Intel

CWD CWD 5

Se (AX)<0, então ( DX) ← FFFFH


DX) ← 0
caso contrário, (

3.4.5.5 nstruções de Ajuste


Estas instruções são utilizadas quando os dados manipulados pelo programa
estão em formato ASCII ou BCD. Tendo em vista que estas são instruções de uso
infreqüente, este trabalho limita-se a mencioná-las. São elas:

AAA AAA 4
DAA DAA 4
AAS AAS 4
DAS DAS 4
AAM AAM 83
AAD AAD 60

3.4.6 Instruções Lógicas

3.4.6.1 E
AND r1,r2 AND AL,BL 3
AND CL,AH
AND r,m AND DL,[SI+5] 9+EA
AND CX,FLAG_WORD
AND m,r AND ASCII[DI],DL 16+EA
(m) ← (m) • (r) AND word ptr [SI+BX+2],AX
AND r,d AND AX,7F7FH 4
(r) ← (r) • d AND CX,0F0H
AND m,d AND BETA,01H 17+EA
(m) ← (m) • d AND byte ptr [BX],6
TEST r1,r2 TEST SI,DI 3
TEST CX,DX

última revisão Ago/2007 22


Linguagem Assembly Família Intel

TEST r,m TEST SI,END_COUNT 9+EA


(r) • (m) TEST CX,[SI]
TEST r,d TEST BX,0CC4H 4/5
(r) • d TEST AL,8
TEST m,d TEST RETURN_CODE,01H 11+EA
(m) • d TEST byte ptr [DI],3

3.4.6.2 OU
OR r1,r2 OR AL,BL 3
OR CL,AH
OR r,m OR DL,[SI+5] 9+EA
OR CX,FLAG_WORD
OR m,r OR ASCII[DI],DL 16+EA
(m) ← (m) + (r) OR word ptr [SI+BX+2],AX
OR r,d OR AX,7F7FH 4
(r) ← (r) + d OR CX,0F0H
OR m,d OR BETA,01H 17+EA
(m) ← (m) + d OR byte ptr [BX],6

3.4.6.3 OU exclusivo
XOR r1,r2 XOR AL,BL 3
XOR CL,AH
XOR r,m XOR DL,[SI+5] 9+EA
XOR CX,FLAG_WORD
XOR m,r XOR ASCII[DI],DL 16+EA
(m) ← (m) ⊕ (r) XOR word ptr [SI+BX+2],AX
XOR r,d XOR AX,7F7FH 4
(r) ← (r) ⊕ d XOR CX,0F0H

última revisão Ago/2007 23


Linguagem Assembly Família Intel

XOR m,d XOR BETA,01H 17+EA


(m) ← (m) ⊕ d XOR byte ptr [BX],6

3.4.6.4 Complemento
NOT r NOT AX 3
NOT CL
NOT m NOT byte ptr [BX] 16+EA
(m)← not (m) NOT word ptr [BX+SI]

3.4.6.5 Deslocamento
SAL/SHL r,1 SAL AL,1 2
SAL/SHL r,CL SHL DX,CL 8+4*(CL)
SAL/SHL m,1 SAL word ptr [BX+2],1 15+EA
SAL/SHL m,CL SHL STORE_COUNT,CL 20+EA+4*(CL)

CF tantas vezes quantas forem definidas pelo


segundo operando

SHR r,1 SHR AL,1 2


SHR r,CL SHR DX,CL 8+4*(CL)
SHR m,1 SHR word ptr [BX+2],1 15+EA
SHR m,CL SHR STORE_COUNT,CL 0+EA+4*(CL
)
CF tantas vezes quantas forem definidas pelo
segundo operando

última revisão Ago/2007 24


Linguagem Assembly Família Intel

SAR r,1 SAR AL,1 2


SAR r,CL SAR DX,CL 8+4*(CL)
SAR m,1 SAR word ptr [BX+2],1 15+EA
SAR m,CL SAR STORE_COUNT,CL 20+EA+4*(CL)

CF

3.4.6.6 Rotação
ROL r,1 ROL AL,1 2
ROL r,CL ROL DX,CL 8+4*(CL)
ROL m,1 ROL word ptr [BX+2],1 15+EA
ROL m,CL ROL STORE_COUNT,CL 0+EA+4*(CL
)
CF tantas vezes quantas forem definidas pelo
segundo operando

ROR r,1 ROR AL,1 2


ROR r,CL ROR DX,CL 8+4*(CL)
ROR m,1 ROR word ptr [BX+2],1 15+EA
ROR m,CL ROR STORE_COUNT,CL 20+EA+4*(CL)

CF tantas vezes quantas forem definidas pelo


segundo operando

última revisão Ago/2007 25


Linguagem Assembly Família Intel

RCL r,1 RCL AL,1 2


RCL r,CL RCL DX,CL 8+4*(CL)
RCL m,1 RCL word ptr [BX+2],1 15+EA
RCL m,CL RCL STORE_COUNT,CL 20+EA+4*(CL)

CF tantas vezes quantas forem definidas pelo


segundo operando

RCR r,1 RCR AL,1 2


RCR r,CL RCR DX,CL 8+4*(CL)
RCR m,1 RCR word ptr [BX+2],1 15+EA
RCR m,CL RCR STORE_COUNT,CL 20+EA+4*(CL)

CF tantas vezes quantas forem definidas pelo


segundo operando

3.4.7 Instruções de Desvio

3.4.7.1 Desvio incondicional


JMP CX
(CS:IP) ← ‘alvo’ JMP dword ptr [SI]
CALL CX
(CS:IP) ← ‘alvo’. CALL dword ptr [SI]
O endereço de retorno é
armazenado na pilha.
RET
(CS:IP)←valor armazenado
no topo da pilha

última revisão Ago/2007 26


Linguagem Assembly Família Intel

RET 8
(CS:IP)←valor armazenado
no topo da pilha.
(SP)←(SP)+d16
INT num_vetor INT 21H 51
(esclarecido noutro capítulo)

INTO INTO 53 ou 4
(esclarecido noutro capítulo)

IRET IRET
(esclarecido noutro capítulo)

3.4.7.2 Desvio condicional

instrução descrição da condição de desvio


JA / JNBE Jump if above/not below nor equal
JAE / JNB Jump if above or equal/not below
JB / JNAE Jump if below/not above
JBE / JNA Jump if below or equal / not above
JC Jump if carry
JE / JZ Jump if equal / zero
JG / JNLE Jump if greater / not less nor equal
JGE / JNL Jump if greater or equal / not less
JLE / JNG Jump if less or equal/ not greather
JNC Jump if not carry
JNE / JNZ Jump if not equal / not zero
JNO Jump if not overflow
JNP / JPO Jump if not parit / parity odd
JNS Jump if not sign
JO Jump if overflow
JP / JPE Jump if parity / parity even
JS Jump if sign
Tabela 1: Instruções de desvio condicional

última revisão Ago/2007 27


Linguagem Assembly Família Intel

JA DESTINO 16 ou 4
Como JMP ‘alvo’, desde JC CX
que ‘cond’ seja satisfeita.

3.4.8 Controle de Iteração


LOOP LABEL 16 ou 5

Se (CX) ≠ 0, (IP)←alvo

LOOPE/LOOPZ alvo LOOPE LABEL 18 ou 6


(CX) ← (CX) -1 LOOPZ LABEL

Se (CX) ≠ 0 e, (ZF) =0
então (IP) ← alvo

LOOPNE/LOOPNZ alvo LOOPE LABEL 19 ou 5


(CX) ← (CX) -1 LOOPZ LABEL

Se (CX) ≠ 0 e, (ZF) ) =0
então (IP) ← alvo
JCXZ alvo JCXZ ROTINA 18 ou 6
Se (CX)=0,
então (IP) ← alvo

3.4.9 Manipulação da Pilha


PUSH r16 PUSH SI 11(reg) ou
10 (seg-reg)

última revisão Ago/2007 28


Linguagem Assembly Família Intel

PUSH m16 PUSH RETURN_CODE[SI] 16+EA

PUSHF PUSHF 10
(SP) ←(SP) -2

((SP)) ←flags

POP r16 POP SI 8

POP m16 POP PARAMETRO 17+EA


(m16) ← ((SP))
(SP) ← (SP) + 2
POPF POPF 8
(flags) ← ((SP))
(SP) ← (SP) + 2

3.4.10 Operações sobre ‘flags’


CLC 2

(CF) ← 00
CMC 2

(CF) ← (CF)

STC 2

(CF) ←00

CLD CLD 2
(DF) ← 0
STD STD 2
(DF) ← 1

última revisão Ago/2007 29


Linguagem Assembly Família Intel

CLI CLI 2
(IF) ← 0
STI STI 2
(IF) ← 1

3.4.11 Instruções de Entrada e Saída


IN AL,d8 IN AL,3FH 10
IN AL,00H
IN AL,DX IN AL,DX 8

IN AX,DX IN AX,DX

OUT d8,AL OUT 24H,AL 10

OUT DX,AX OUT DX,AX 8

OUT DX,AL OUT DX,AL

3.4.12 "NO OPERATION"


NOP NOP 3
não realiza nenhuma
operação

3.4.13 Outras Instruções

Para tornar esta relação completa, serão mencionadas instruções, cuja


compreensão demanda conhecimento ainda não disponíveis ao estudante nesta
altura do curso. Por este motivo não será apresentada uma descrição destas
instruções. São elas: ESC, HLT, LOCK, WAIT.
última revisão Ago/2007 30
Linguagem Assembly Família Intel

4 Subrotinas

A Figura 9 ilustra algumas técnicas que são utilizadas para escrever


subrotinas em Assembly 86. Neste exemplo um programa chama uma subrotina
(chamada EXEMPLO) passando a ela um vetor de bytes. Dois parâmetros são
passados na pilha: o primeiro contém o número de elementos do vetor, e o segundo
contém o endereço do primeiro elemento do vetor. Desta forma, embora a subrotina
receba sempre dois parâmetros, eles podem ser usados para referenciar
indiretamente qualquer número de variáveis na memória.
Os resultados que a rotina retorna devem estar em registradores ou em
memória, mas não podem estar na pilha. AX ou AL são freqüentemente utilizados
para conter o resultado de uma rotina quando se trata de um byte ou palavra. Este é
comumente o caso das chamadas functions. Alternativamente o programa principal
pode passar à subrotina como parâmetro o endereço (ou endereços) de uma área
da memória onde deve ser escrito o resultado.
Cada linguagem de programação de alto nível tem uma convenção diferente
para passagem de parâmetros na pilha. A convenção aqui apresentada é, portanto,
apenas um exemplo.
A Figura 10a mostra como está a pilha antes do programa principal
armazenar os parâmetros na pilha. A Figura 10b mostra a pilha como a subrotina a
vê, logo após a execução da instrução CALL.
A rotina EXEMPLO pode ser dividida em quatro seções. o prólogo, em que o
reigstrador SP é preparado para ser utilizado como registrador de base nos acessos
aos dados contidos na pilha. O próximo passo no prólogo é salvar o estado da
máquina. Isto é feito armazenando na pilha todos os registradores alterados pela
subrotina (no caso, apenas CX e BX). Se a subrotina modifica os flags, e o
programa principal conta com que os flags estejam inalterados após a execução da
subrotina, o registrador de estado também deve ser armazenado na pilha.
A última instrução do prólogo aloca três palavras na pilha para serem usadas
como variáveis locais. A Figura 10c mostra a pilha após o prólogo. O conjunto de
palavras alocadas na pilha para esta rotina é chamado de “quadro” (“frame”) da
rotina.
O corpo da rotina faz o processamento propriamente dito, que, em
EXEMPLO, corresponde a nenhum processamento.
Os parâmetros na pilha são endereçados relativamente ao registrador BP,
como mostra a Figura 10.

última revisão Ago/2007 31


Linguagem Assembly Família Intel

EX_SEG SEGMENT
VETOR DB 10 DUP(?)

ASSUME CS:EX_SEG,DS:EX_SEG;SS:EX_SEG;ES:NOTHING
EXEMPLO PROC NEAR
;Prólogo
PUSH BP ; salva BP
MOV BP,SP ; estabelece base
PUSH CX ; salva estado
PUSH BX ; “ “
PUSHF ; e flags
SUB SP,6 ; aloca espaço para variáveis locais
; fim do prólogo
; Corpo
MOV CX,[BP+6] ; pega nº de elementos
MOV BX,[BP+4] ; pega endereço inicial do vetor
ƒ
ƒ
ƒ
; variáveis locais estão em [BP-8], [BP-10] e [BP-12]
; fim do corpo
;Epílogo
ADD SP,6 ; desaloca variáveis locais
POPF ; restaura flags
POP BX ; restaura estado
POP CX ; “ “
POP BP ; “ “
; fim do epílogo
;Retorno da subrotina RET 4
EXEMPLO ENDP

; Programa principal
START: MOV AX, SIZE VETOR
PUSH AX
MOV AX,OFFSET VETOR
PUSH AX
CALL EXEMPLO
ƒ
ƒ
ƒ
EX_SEG ENDS
END START
Figura 9: Exemplo do uso de passagem de parâmetros e criação de variáveis locais
pela pilha.

última revisão Ago/2007 32


Linguagem Assembly Família Intel

O epílogo desaloca o espaço das variáveis locais e restaura o valor antigo do


estado da máquina. A Figura 10d mostra a configuração na pilha logo após o
epílogo, pouco antes da instrução de retorno. A última seção da rotina é o retorno:
instrução RET 4, além de restaurar o antigo valor de IP, descarta os parâmetros de
entrada, acrescentado 4 ao conteúdo de SP.

endereço
baixo →

Endereço
alto → Í SP

(a) (b) ( c) (d)


Figura 10: Condição da pilha: (a) antes do programa principal armazenar os
parâmetros; (b) após a instrução CALL; ( c) após o prólogo; (d) após o epílogo
(antes de RET)

5 Codificação das Instruções de Máquina do 8086/8088

Esta seção fornece uma idéia de como as instruções codificadas em linguagem


Assembly são traduzidas para as instruções de máquina. O texto apresentado a
seguir é praticamente uma tradução de parte do “The 8086 Family User’s Manual”.
A instrução em ASM-86 "MOV" pode assumir 28 formas diferentes. Um
programador raramente precisa conhecer os detalhes da codificação das instruções
para escrever programas. Quando se deseja, por exemplo, depurar um programa a
partir de informações obtidas pela monitoração das barras do sistema através de
algum aparelho qualquer. Nesta seção são apresentadas as informações necessárias
para traduzir ou decodificar uma instrução do 8086/8088. O esquema aqui
apresentado é o mesmo dos processadores posteriores da família (80286, 80386,
80486, Pentium etc.), a menos de algumas extensões que foram incorporadas às
novas versões.
Uma instrução de máquina pode ocupar entre 1 e 6 bytes. Instruções de 1 byte
normalmente operam sobre um único registrador ou sinalizador e são facilmente
última revisão Ago/2007 33
Linguagem Assembly Família Intel

identificáveis. Nas instruções mais longas são os primeiros dois bytes que contém a
chave para a decodificação. O formato destes bytes varia mas a maioria das intruções
segue o formato apontado na Figura 11

byte 1 byte 2 byte 3 byte 4 byte 5 byte 6

Figura 11: Formato típico de uma instrução de máquina do 8086/8088

Os primeiros seis bits de uma instrução de mais de 1 byte geralmente contém o


código da operação ("opcode") que identifica o tipo básico da instrução: ADD, XOR,
etc. O bit seguinte, o campo D, geralmente especifica a direção da operação: 1 → o
campo REG no segundo byte identifica o operando de destino, 0 → o campo REG
identifica o operando fonte. O campo W distingui entre operações sobre 8 bits ou 16
bits: 0 = byte, 1 = palavra de 16 bits.
Um entre três campos de um bit, S, V, ou, Z aparece no formato de algumas
instruções. S é usado em conjunto com W para indicar a extensão do campo imediato
nas instruções aritméticas. V distingui entre deslocamentos e rotações de uma ou
várias posições. Z é usado como bit de comparação com o indicador ZF (zero) nas
instruções de repetição e iterações condicionais. A Tabela 2 mostra um resumo do
significado associado a cada um destes campos de um bit.
O segundo byte da instrução usualmente identifica os operandos da instrução.
O campo MOD indica, se um dos operandos está na memória ou, se ambos os
operandos são registradores (veja Tabela 3).
O campo REG identifica o registrador que conte um dos operandos. Sua
codificação é igual à no campo R/M para MOD=11 (3 primeiras colunas da Tabela 3
Em várias instruções, principalmente as do tipo "operando imediato para a
memória", REG é usado como uma extensão do "opcode" para identificar o tipo de
operação. A codificação do campo R/M (registrador/memória) depende do conteúdo
última revisão Ago/2007 34
Linguagem Assembly Família Intel

do campo MOD. Se MOD = 11 (modo registrador-registrador), R/M identifica o


segundo registrador operando. Se MOD seleciona o modo memória, então o campo
R/M indica como deve ser calculado o endereço efetivo do operando.

campo valor função


S 0 nenhuma estensão do sinal
1 sinal do valor de 8 bits é extendido se W=1
W 0 a instrução opera sobre byte
1 a instrução opera sobre palavra de 16 bits
D 0 o campo REG identifica o operando fonte
1 o campo REG identifica o operando destino
V 0 deslocamento/rotação de uma posição
1 deslocamento/rotação especificado em CL
Z 0 repetição/iteração, enquanto ZF = 0
1 repetição/iteração, enquanto ZF = 1
Tabela 2: Codificação dos campos de um bit

Os bytes 3 a 6 de uma instrução, quando estão presentes, contém usualmente


o valor do deslocamento do operando na memória e/ou o valor de um operando
imediato.
Pode haver um ou dois bytes contendo o deslocamento do endereço efetivo do
operando. O campo MOD indica quantos campos deslocamento estão presentes.
Seguindo a convenção da Intel, "little endian", se o deslocamento contém dois bytes, o
byte mais significativo é armazenado em segundo lugar na instrução. Se o
deslocamento é apenas um único byte, o 8086/8088 estende este valor internamente
mantendo o sinal, para um valor de 16 bits, para então utilizá-lo nos cálculos de
endereço. Valores imediatos sempre vêm depois dos valores de deslocamento,
quando presentes. O segundo byte de um valor imediato de dois bytes é o mais
significativo. O data sheet deste processador possui tabelas com a decodificação de
cada instrução, baseada na descrição apresentada.

MOD=11 Cálculo do Endereço Efetivo


R/M W=0 W=1 MOD=00 MOD=01 MOD=10
000 AL AX (BX)+(SI) (BX)+(SI)+d8 (BX)+(SI)+d16
001 CL CX (BX)+(DI) (BX)+(DI) +d8 (BX)+(DI) +d16
010 DL DX (BP)+(SI) (BP)+(SI) +d8 (BP)+(SI) +d16
011 BL BX (BP)+(DI) (BP)+(DI) +d8 (BP)+(DI) +d16
100 AH SP (SI) (SI) +d8 (SI) +d16
101 CH BP (DI) (DI) +d8 (DI) +d16
110 DH SI end. direto (BP)+d8 (BP)+d16
111 BH DI (BX) (BX) +d8 (BX) +d16
Tabela 3: Codificação dos modos de endereçamento do 8086/8088

última revisão Ago/2007 35


Linguagem Assembly Família Intel

6 Formação do Endereço no 8086/8088

No 8086/88 o conteúdo do registrador apontador de programa - IP - não é


propriamente o endereço da próxima instrução a ser executada, mas a diferença
("offset") entre o endereço da próxima instrução a ser executada e um determinado
endereço inicial, conforme ilustra a Figura 12.

Figura 12: Relação entre o endereço lógico e físico no 8086/88

De modo semelhante, o endereço efetivo de um operando calculado pela EU


do 8086/88 não é o endereço físico da posição de memória que contém o operando,
mas a diferença ("offset") entre o endereço da posição de memória que contém o
operando e um determinado endereço inicial. A este endereço inicial dá-se o nome de
endereço de início de segmento ou endereço de base.
Tanto num caso como no outro, o endereço de início de segmento está em um
dos registradores de segmento (CS, DS, SS ou ES).
O endereço efetivo calculado pela EU, bem como o conteúdo do apontador de
programa - IP, são valores de 16 bits. Isto implica em que, usando um mesmo
endereço de início de segmento pode-se endereçar até 64 Kbytes de memória. A área
contígua de memória endereçada pelo 8086/88, ao executar um programa, utilizando
um mesmo endereço de início de segmento é chamada segmento.
A Figura 13 mostra como é formado o endereço físico das instruções no
8086/88. O endereço de início de segmento é, no caso de instruções, sempre definido
pelo conteúdo do registrador CS ("code segment register") e o "offset" pelo conteúdo
do registrador IP ("instruction pointer").
Para formar o endereço da instrução, o 8086/88 multiplica o conteúdo do registrador
de segmento CS por 16D e adiciona o resultado ao conteúdo do registrador IP. Note
que a multiplicação por 16D tem o mesmo efeito de deslocar o conteúdo de CS 4 bits à
esquerda, preenchendo os 4 bits menos significativos com "0s".

última revisão Ago/2007 36


Linguagem Assembly Família Intel

Figura 13: Formação do endereço físico no 8086/88 para acesso a código.

No caso dos endereços de operandos, o microprocessador busca o endereço


de início de segmento em qualquer dos registradores de segmento (CS, DS, SS e
ES). Já o "offset" dos endereços de operandos pode envolver até 3 componentes:
• Base,
• Índice, e
• Deslocamento

A Figura 14 ilustra como o 8086/88 calcula o "offset" e como é formado o


endereço físico dos operandos.
Os fatores base, índice e deslocamento podem ser contabilizados ou não para o
cálculo do "offset" dependendo do modo de endereçamento utilizado pela instrução
executada.
Uma questão a ser ainda esclarecida é: num acesso a um operando qual dos
registradores de segmento deve ser usado para determinar o endereço de início de
segmento? De um modo geral vale a seguinte regra:
última revisão Ago/2007 37
Linguagem Assembly Família Intel

Figura 14: Formação do endereço no 8086/800 (a) para acesso a código, (b) para
acesso a operando.

• para cesso a código usa-se sempre o registrador CS,


• para acesso a dados fora da pilha usa-se normalmente
DS ,
• para acesso a dados na pilha usa-se SS ,
o conteúdo do registrador BP é usado para compor o endereço efetivo do
operando, o registrador de segmento é o SS
Existem instruções, no entanto, que utilizam o registrador ES ao invés do
registrador DS para acessar dados. As instruções de manipulação de "strings"
implicam no uso do registrador ES na formação do endereço efetivo do operando nos
acessos de escrita. Desta forma a instrução MOVS transfere o byte ou palavra contida
no endereço (DS:SI) para o endereço (ES:DI). De modo semelhante a instrução
CMPS compara o byte ou palavra contido no endereço (DS:SI) com o byte ou palavra
do endereço (ES:DI). Também a instrução SCAS compara o conteúdo do acumulador
(registrador AL ou AX) com o conteúdo do endereço definido por (ES:DI).
última revisão Ago/2007 38
Linguagem Assembly Família Intel

Além disso, é possível forçar o microprocessador a utilizar no cálculo do


endereço efetivo um registrador de segmento diferente do previsto pela regra acima
("default"). Nestes casos a instrução recebe um prefixo que impõe a utilizarão de um
registrador de segmento distinto do definido por essas regras.
A instrução

código instrução clocks


88 9C 00 40 9+EA

por exemplo, move o conteúdo da posição de memória, cujo endereço efetivo é dado
pelo conteúdo do registrador DS ("default" neste caso) deslocado 4 bits à esquerda,
adicionado ao valor do deslocamento, que neste caso é o endereço do primeiro byte
do vetor ARRAY, adicionado ainda ao conteúdo do registrador de índice SI, para o
registrador BL. Se, ao invés de utilizar o registrador de segmento "default", se deseja
utilizar, digamos, o registrador ES, a instrução em Assembly toma a seguinte forma:

código instrução clocks


26 88 9C 00 40 9+EA

Há casos, no entanto, em que não se pode utilizar outro registrador de


segmento diferente do "default". Por exemplo, nos acessos a instrução o registrador
CS é sempre utilizado. Da mesma forma, quando se acessa a pilha utilizando o
registrador apontador de pilha - SP - necessariamente se utiliza o registrador SS como
registrador de segmento.
O conteúdo dos registradores de segmento é normalmente definido no início da
execução do programa, utilizando instruções apropriadas. Nada impede porém que o
conteúdo dos registradores de segmento seja alterado várias vezes ao longo do
programa.
A grande vantagem do uso destes registradores de segmento é que tanto os
códigos como os dados e a pilha podem ser carregados em qualquer região da
memória, sem que seja necessário modificar o código objeto. Basta carregar os
registradores de segmento de modo adequado.
O sistema operacional de um microcomputador pessoal, por exemplo, antes de
carregar um programa na memória, verifica qual a região da memória que está
disponível naquele momento e carrega o programa da memória de massa naquela
região. Antes de passar o controle para o programa carregado, o sistema operacional
carrega os registradores de segmento com os valores adequados. Na maioria dos
microprocessador de 8 bits, como o 8085, por exemplo, os endereços estão fixos no
código objeto. Para se alterar a região de memória onde um programa do 8085 será
carregado é necessário alterar o código objeto.
Programas que podem ser carregados em qualquer região da memória sem
que o código objeto precise ser alterado, são chamados programas relocáveis.

última revisão Ago/2007 39


Linguagem Assembly Família Intel

7 O Mecanismo de Segmentação e as Instruções de Desvio

Evidentemente o mecanismo de segmentação da memória tem implicações


sobre o conjunto de instruções do microprocessador.
O primeiro aspecto a abordar aqui diz respeito às instruções de desvio: JMP,
CALL e RET. Existem dois tipos de desvio: o intra-segmento e o inter-segmento.

7.1 Desvios intra-segmento


Numa instrução JMP intra-segmento o endereço para onde o programa deve
ser desviado, pertence ao mesmo segmento onde está armazenada a instrução JMP
em execução. Neste caso, a instrução deve conter, além do código do JMP, a
especificação do novo valor do registrador IP. O seguintes exemplos são
esclarecedores:

código instrução clocks


E9 00 40 15
FF E3 11
FF A7 00 40 18+EA

No primeiro exemplo o valor a ser carregado no IP é definido na própria


instrução. O valor codificado na instrução neste caso é na realidade a diferença entre
o conteúdo atual e o novo conteúdo de IP. O que o microprocessador faz na prática
é somar este valor contido na instrução ao conteúdo de IP. Este tipo de desvio é
chamado desvio direto.
No segundo e terceiro exemplos tem-se desvios indiretos. No segundo caso o
conteúdo do registrador BX é copiado no registrador IP. No terceiro exemplo carrega-
se em IP a palavra armazenada no endereço de memória especificado na instrução.
Em todos os exemplos acima se diz que o desvio é do tipo próximo (NEAR), um
sinônimo de desvio intra-segmento.
Deve-se distinguir um tipo de instrução de desvio intra-segmento. Quando a
diferença entre o endereço que contém a instrução JMP e o endereço de desvio está
entre -128 e 127, pode se utilizar o desvio curto (SHORT). Esta situação é muito
frequente em pequenos "loops". Neste caso é especificado na instrução o código da
instrução JMP e a diferença entre o valor atual e o novo valor do registrador IP
expresso em 8 bits. Este tipo de desvio é chamado desvio relativo. Exemplo:

última revisão Ago/2007 40


Linguagem Assembly Família Intel

código instrução clocks


EB 0F 15

Neste caso ao conteúdo de IP é acrescido o valor 0FH. Observe que o código


neste caso é mais compacto.

código instrução clocks


E9 78 01 15

Observe que o primeiro byte do código do JMP é diferente em cada caso.

7.2 Desvios inter-segmento


Diz-se que o desvio é do tipo inter-segmento, quando o endereço para onde o
programa deve desviar está em outro segmento. Neste caso é preciso especificar na
instrução os novos valores para os registradores IP e CS, como nos exemplos abaixo.

código Instrução clocks


EA 00 30 34 02 15
FF AC 28 00 24+EA

No primeiro exemplo têm-se um desvio direto, e no segundo caso um desvio


indireto. Diz-se em ambos os casos que o desvio é do tipo distante (FAR).

7.3 Desvios condicionais


Para desvios condicionais o processador só permite desvios do tipo curto
direto, em que a distância entre a instrução e o endereço de desvio está na faixa -128
e 127. Exemplo:

código Instrução clocks


76 3F 16 ou 4

última revisão Ago/2007 41


Linguagem Assembly Família Intel

7.4 Instruções CALL


A subrotina para instruções CALL é semelhante. Há 5 formas distintas de se
chamar uma subrotina, conforme mostram os exemplos a seguir.

código Instrução clocks


E8 00 40 ; direto próximo 19
FF D3 ; indireto próximo 16
FF 10 ; indireto próximo 21+EA
9A 00 03 27 11 ; direto longo 28
FF AD 00 04 ; indireto longo 37+EA

Uma diferença importante entre os CALLs próximos e distantes deve ser


mencionada aqui. Instruções CALL intra-segmento (próximas) salvam na pilha apenas
o conteúdo do IP para o retorno, portanto uma palavra de 16 bits. Quando ocorre um
CALL inter-segmento (distantes), são salvos na pilha tanto o conteúdo de IP quanto o
de CS, portanto duas palavras de 16 bits.

7.5 Instruções RET


De modo análogo ao que ocorre com a instrução CALL, existem dois tipos de
instruções de retorno: o primeiro para o retorno de subrotinas distantes e outro para o
retorno de subrotinas próximas. As instruções do primeiro tipo restauram apenas o
conteúdo do IP, enquanto as do segundo tipo restauram tanto IP quanto CS.
Exemplos:

código instrução clocks


CB ; intra-segmento (próximo 8
CA 08 00 ; intra-segmento (próximo) 12
C3 ; inter-segmento (distante) 18
C2 08 00 ; inter-segmento (distante) 17

Deve-se deixar claro que o programador não precisa se preocupar com o tipo
de instrução a utilizar em cada caso. O próprio montador Assembly identifica qual é o
tipo de instrução adequado para cada situação e produz o código correspondente.
Antes de concluir esta seção cabe relacionar este assunto com o que foi

última revisão Ago/2007 42


Linguagem Assembly Família Intel

discutido na seção 3, sobre convenção de passagem de parâmetro e alocação de


variáveis locais em subrotinas. O exemplo considerado então se baseava numa rotina
próxima. Se a subrotina EXEMPLO fosse do tipo distante a instrução CALL
armazenaria na pilha os valores de CS e de IP. A distância do conteúdo de BP em
relação a cada parâmetro de entrada seria maior, mais exatamente acrescida de 2.

8 Operação EU + BIU - Pré-busca de Instruções


Já foi mencionado no início do texto que o 8086/8088 é constituído de duas
unidades capazes de operar independentemente. A EU, que cuida exclusivamente da
execução das instruções, e a BIU, que cuida exclusivamente da comunicação com o
restante do sistema. Quando a EU necessita alguma informação da memória, seja
para ler o código de uma instrução ou ler/escrever um operando, ela gera uma
solicitação à BIU que providencia a execução do acesso.
Associada à BIU existe uma fila de instruções com capacidade de 6 bytes no
8086 e 4 bytes no 8088, onde a BIU vai armazenando códigos de instruções que
ainda não começaram a ser executadas, mas que o serão "provavelmente" logo em
seguida. Na verdade a BIU vai carregando bytes armazenados na memória em
endereços consecutivos àquele que contém a instrução presentemente sendo
executada. Devido à tendência do microprocessador de executar instruções na
mesma ordem em que estas estão armazenadas na memória, há uma alta
probabilidade de que, ao a EU solicitar à BIU o código da próxima instruções, este já
se encontre armazenado na fila de instruções. Neste caso a EU não necessitará
aguardar ociosa até que a BIU vá à memória buscar as instruções, para só então
iniciar sua execução. Ao contrário, a EU poderá iniciar imediatamente a execução da
instrução.
O esquema de pré-busca de instruções executado pela BIU erra na estimativa
de qual será a próxima instrução a ser executada quando ocorrem instruções de
desvio. Nestes casos a BIU providencia o esvaziamento da fila de instruções e passa
a carregar bytes/palavras da memória a partir do endereço da nova instrução.
A BIU inicia um acesso de pré-busca de instrução sempre que as seguintes
duas condições estiverem satisfeitas:
1. há pelo menos 2 bytes (1 byte no caso do 8088) livres na fila
de instruções.
2. não existe nenhuma solicitação de acesso ao barramento
pendente vinda da EU.

A Figura 15 ilustra as ações do 8086 e o conteúdo da fila de instruções quando


o 8086 executa uma instrução. Admite-se que esta instrução esteja contida em dois
bytes e que a fila se encontra vazia imediatamente antes do início da execução. Esta
instrução gasta 7 ciclos de relógio para calcular o endereço efetivo do operando que
se encontra na memória e mais 6 ciclos de relógio para executar a operação.
A EU está ativa quando está executando instruções e inativa quando espera
última revisão Ago/2007 43
Linguagem Assembly Família Intel

que a BIU lhe forneça um dado ou instrução. A BIU só está ativa quando atende
alguma solicitação da EU ou quando há pelo menos 2 (no 8088 1) bytes livres na fila
de instruções. Quando estas duas condições estão satisfeitas ao mesmo tempo, a
prioridade é sempre dada à solicitação da EU. Quando chega uma solicitação da EU
enquanto a BIU realiza uma busca de instrução, a solicitação da EU só passa a ser
atendida depois de que o acesso que está em andamento for completado. Neste caso
a EU é forçada a esperar ociosa.

1º ciclo 2º ciclo 3º ciclo 4º ciclo 5º ciclo 6º ciclo

E
U

B
I
U

Figura 15: Diagrama de atividades da EU e da BIU

9 Tempo de Execução das Instruções

O exemplo apresentado na seção anterior torna claro que o tempo consumido


na execução de um programa depende tanto das instruções como da seqüência em
que estas instrução são executadas. Um mesmo conjunto de instruções pode ter um
tempo de execução distinto dependendo da ordem em que as instruções do conjunto
são executadas. Esta característica dificulta a tarefa de determinar por inspeção de
um programa em Assembly o tempo de execução correspondente.
Nesta seção é descrito um procedimento que permite estimar o tempo de
execução de um programa a partir de uma simples inspeção das instruções que o
constituem.
Na descrição das instruções nas seções anteriores, é apresentado no lado
direito de cada uma o número de ciclos de relógio necessários para executá-la. Nas
instruções que envolvem operandos da memória "+EA" denota o número de ciclos de
relógio (clocks) adicionais necessários para calcular o endereço efetivo do operando.
última revisão Ago/2007 44
Linguagem Assembly Família Intel

A Tabela 4 apresenta os tempos de cálculo do endereço efetivo em número de ciclos


de relógio.
Para as instruções de desvio, os valores incluem os ciclos adicionais para
reiniciar a fila de instruções, bem como o tempo necessário para trazer da memória a
instrução contida no endereço de destino. Para os desvios condicionais são dados
dois valores separados por um "/". O valor mais alto corresponde à situação em que o
desvio se consuma e o valor mais baixo à situação contrária.

Componentes do Endereço Efetivo clocks


somente deslocamento 6
somente base ou índice 5

deslocamento + base ou índice 9

base + índice BX+SI ou BX+DI 7


BP+SI, BP+ DI 8
deslocamento BP+DI+deslocamento 11
+ base BX+SI+deslocamento 11
+ índice BP+SI+deslocamento 12
BX+DI+deslocamento 12
Tabela 4: Tempo consumido no cálculo do endreço efetivo dos operandos em número
de ciclos de relógio.

Além disso, deve-se acrescentar 4 ciclos de relógio para cada instrução que
referencia uma palavra de 16 bits contida num endereço ímpar, pois o 8086 só é
capaz de ler uma palavra de 16 bits num único acesso se o byte menos significativo
da palavra estiver num endereço par. Se esta condição não for satisfeita, o 8086 gera
dois acessos, cada um referenciando uma das duas metades da palavra.
Quando se utiliza um 8088 deve ser acrescentado 4 ciclos para todos os
acessos a operandos de 16 bits, independentemente de estarem ou não
armazenados em endereços pares.
Quando se utiliza um prefixo definindo um registrador de segmento diferente do
"default", devem ser acrescentados mais dois ciclos de relógio.
O tempo total de execução de um trecho de programa pode ser obtido
multiplicando-se o tempo de execução de cada instrução particular pelo número de
vezes que ela é executada. A soma destes produtos produz uma estimativa otimista,
mas bastante aproximada do valor real.
Vários fatores podem aumentar o tempo de execução relativamente a esta
estimativa. Estes tempos admitem que as instruções estão disponíveis na fila de
instruções assim que a EU está pronta para executá-las. Quando se tem uma
sequência de instruções rápidas (com menos do que 2 ciclos de relógio) a fila de
instruções pode esvaziar e obter-se um tempo de execução maior.
Um outro aspecto evidenciado no exemplo da seção anterior, é que a interação

última revisão Ago/2007 45


Linguagem Assembly Família Intel

entre a EU e a BIU pode ter efeitos sobre o tempo de execução. Se a EU necessita


buscar um operando da memória, ela poderá ter que esperar até que a BIU complete
o acesso em andamento antes de iniciar o acesso solicitado pela EU. Eventuais
ciclos espera no acesso a operandos podem também afetar o tempo de execução
(vide exercício 27).
A partir de experimentos realizados pelo fabricante do integrado, concluí-se,
contudo, que este método de estimativa produz valores próximo dos valores reais. Os
tempos de execução para outros processadores da família podem ser obtidos nos
data sheets específicos. O procedimento para estimativa é, contudo,
aproximadamente o mesmo para todos eles.

10 Exercícios
1ª Questão
Sejam m e k dois números hexadecimais de 32 bits armazenados respectivamente
nos registradores DX:AX e CX:BX, de tal forma que AX e BX contém os 16 bits
menos significativos de m e k respectivamente. Escreva um trecho de programa em
ASM86 que subtraia k de m e coloque o resultado em DX:AX. Se m < k o resultado
produzido deve ser zero.

2ª Questão
Repita o exercício anterior admitindo que m e k estão armazenados respectivamente
a partir dos endereços MMMM e KKKK. DX:AX deve conter o resultado.

3ª Questão:
Escreva um trecho de programa em ASM86 que multiplique o conteúdo do
registrador AL pelo conteúdo do registrador CL, deixando o resultado em AX. Você
não poderá usar a instrução MUL ou IMUL. Utilize sucessivas adições.

4ª Questão:
Escreva um trecho de programa em ASM86 que divida o conteúdo do registrador DX
pelo conteúdo do registrador AX, deixando o quociente inteiro em AX. Você não
poderá usar a instrução DIV ou IDIV. Utilize sucessivas subtrações.

5ª Questão:
Escreva um trecho de programa em ASM86 que eleve o conteúdo do registrador AX
ao conteúdo do registrador DX. O resultado de 32 bits deve ser colocado no par de
registradores CX:AX. Caso o valor obtido não possa ser representado em 32 bits, o
conteúdo de DX:AX deverá ser nulo.

última revisão Ago/2007 46


Linguagem Assembly Família Intel

6ª Questão:
Escreva um trecho de programa em ASM86 que zere todo o registrador AL, se o seu
nibble2 menos significativo for igual a zero e carregue o valor 0FFH em AL, caso
contrário.

7ª Questão:
Escreva um trecho de programa em ASM86 que produza nos bits de ordem ímpar os
correspondentes bits do registrador BL, e nos bits de ordem par de AL os
correspondentes bits de CL.

8ª Questão:
Escreva um trecho de programa em ASM86 que produza nos bits de ordem par de
AL o ou exclusivo dos bits correspondentes dos registradores BL e CL, e nos bits de
ordem ímpar mantenha o mesmo valor inicial.

9ª Questão:
Escreva um trecho de programa em ASM86 que faça o ou exclusivo entre os dois
nibbles2 do registrador AL.

10ª Questão:
Escreva um trecho de programa em ASM86 que transforme um número em formato
BCD armazenado em AL no seu correspondente valor hexadecimal Você não pode
usar as instruções de multiplicação, divisão, e de ajuste a BCD.

11ª Questão:
Escreva um trecho de programa em ASM86 devolva no registrador CL a paridade do
conteúdo de AX, isto é, se o número de 1s em AX for ímpar, CL=0, e CL=1, caso
contrário.

12ª Questão:
Você tem uma região da memória no segmento apontado por DS do tipo:
BUFFER DB 80 DUP(?)
onde são armazenados strings de caracteres vindos de um teclado. Um string pode
ter no máximo 80 caracteres e é sempre terminado pelo caracter "CR" (0DH).
Escreva uma subrotina em ASM86 que determine o tamanho do string desta região.
Admita que os registradores de segmento estão convenientemente carregados.

13ª Questão:
A tabela abaixo é construída de tal maneira que o conteúdo de i-ésimo elemento é
exatamente o correspondente código EBCEDIC de i, onde i está codificado em
ASCII.
CONV_TAB DB 64 DUP(0H)
DB 1 20H
DB 9 DUP(0H)
2
Nibble é o termo em inglês que denota os quatro bits de cada uma das metades de um byte.
última revisão Ago/2007 47
Linguagem Assembly Família Intel

DB 7 'ç','.','<','(','+',0h,'&'
DB 9 DUP(0H)
DB 8 '!','$','*',')',';',' ','-','/'
DB .....

Você tem ainda duas regiões da memória no segmento apontado por DS do tipo:

BUFFER_ASCII DB 80 DUP(?)
BUFFER_EBCDIC DB 80 DUP(?)

Na primeira região são armazenados strings de caracteres vindos de um teclado. Um


string pode ter no máximo 80 caracteres e é sempre terminado pelo caracter "CR"
(0DH). Escreva um programa em ASM86 que coloque na segunda região de
memória o string produzido pelo teclado, devidamente traduzido para o código
EBCDIC.

14ª Questão:
Embora o 8086/8088 possua instruções de multiplicação e divisão inteira, escreva
um programa (MULT) que multiplique dois números inteiros sem sinal m,
armazenado em AH, e k, armazenado em AL, utilizando o seguinte algoritmo

MULT(m,k)=[(k x 20 x m0]+[(k x 21 x m1]+...[(k x 27 x m7]

O resultado deve estar no registrador AX.

15ª Questão:
Escreva um programa que divida o conteúdo de AX pelo conteúdo de AL, utilizando
algoritmo análogo ao do exercício anterior, sem usar as instruções de divisão do
8086/8088. O quociente deve ser colocado em AL e o resto em AH.

16ª Questão:
Escreva uma rotina (SROT) que tome o parâmetro de entrada, um valor de 16 bits, e
troque de posição o byte mais e o byte menos significativo, retornando este valor em
AX. O parâmetro de entrada é passado para a subrotina SROT pelo programa
principal através da pilha, utilizando o mecanismo de passagem de parâmetros visto
neste material.

17ª Questão:
Escreva uma subrotina (TWOCP) em ASM-86 que calcule o complemento de dois de
um valor de 32 bits contido nos registradores DX:AX. O valor complementado deve
retornar no próprio par de registradores DX:AX. O conteúdo dos demais
registradores não pode ser modificado por TWOCP.

18ª Questão:
Escreva uma subrotina (LOG) em ASM-86 que calcule o ln(x+1). O valor de x é um
número inteiro menor do que 256, expresso em 16 bits e é passado a LOG pela
última revisão Ago/2007 48
Linguagem Assembly Família Intel

pilha, usando o método apresentado neste material. O resultado deverá ser devolvido
em 16 bits ao programa principal no registrador AX. Utilize a aproximação de Taylor:
x2 x3 x 4 x5
ln( x + 1) = x − + − +
2 3 4 5
Você já dispõe da subrotina POWER que eleva o conteúdo do primeiro parâmetro,
ao valor do segundo parâmetro retornando o resultado em AX. Ambos os parâmetros
são valores inteiros menores do que 256,expressos em 16 bits, passados através da
pilha. POWER não altera o conteúdo de nenhum registrador a menos de AX. Se
ocorrer overflow, seu programa deve retornar o valor zero. Crie uma variável
temporária que contenha os valores parciais do cálculo do logaritmo.

19ª Questão:
Escreva uma rotina (INTER) em ASM-86 que determine a intersecção entre dois
conjuntos de números de 8 bits. Os parâmetros de entrada são todos expressos em
16 bits e são passados à rotina na seguinte ordem: endereço inicial do vetor
contendo o primeiro conjunto, número de elementos do primeiro conjunto, endereço
inicial do vetor contendo o segundo conjunto, número de elementos do segundo
conjunto, endereço inicial do vetor contendo o conjunto intersecção. Além disso, a
subrotina deve retornar em AX a cardinalidade do conjunto intersecção. Admita que
não há repetições de números num mesmo conjunto, e que ambos os conjuntos têm
pelo menos um elemento.

20ª Questão:
Escreva a rotina (MEDIA) em ASM-86 que calcule a média de n números (x1, x2, ...,
xn) inteiros, sem sinal, expressos em um byte. Os parâmetros - endereço a partir de
onde estão armazenados os números e n - são expressos em 16 bits e são
passados a MEDIA pela pilha, conforme a convenção vista em aula. O resultado, de
16 bits, deve ser passado ao programa principal pelo registrador AX. O flag Carry,
deverá quando setado que houve overflow.

21ª Questão:
Escreva uma rotina (SORT) em ASM-86 que faça o sort dos bytes contidos num
determinado vetor. O endereço inicial do vetor, bem como o número de elementos
deste vetor, são passados à rotina pela pilha. Os elementos do vetor são números
inteiros com sinal. Utilize variáveis auxiliares locais.

22ª Questão:
Escreva a rotina MAXIMO em ASM86 que determine o maior de um conjunto de n
números inteiros sem sinal expressos em 16 bits. O valor de n, expresso em 16 bits,
e os n bytes são passado a MAXIMO pela pilha, conforme o método discutido neste
material. O maior valor encontrado deve retornar no registrador AX. Escreva a rotina
de modo que todos os parâmetros de entrada sejam retirados da pilha antes de
retornar ao programa principal. Utilize uma variável local para conter o valor máximo
temporário.

última revisão Ago/2007 49


Linguagem Assembly Família Intel

23ª Questão: Escreva uma subrotina (ESCALAR) em ASM-86 que execute o produto
escalar entre dois vetores de inteiros de 16 bits com sinal. O tamanho dos vetores
(expresso em 16 bits) e o endereço inicial de cada um dos dois vetores de entrada,
bem como o do endereço do vetor de saída são passados à rotina pela pilha. O
resultado deve retornar ao programa principal pelo registrador AX. Admita que não
haverá overflow nos cálculos parciais. Utilize variável(eis) local(is).

24ª Questão
O programa abaixo soma as palavras contidas numa tabela armazenada na memória
a partir do endereço TABLE, deixando o resultado no registrador AX. Codifique este
trecho de programa em ASM86.

SUB AX,AX
MOV CX,25 ;carrega CX c/ tamanho da tabela
MOV SI,50
ADD_NEXT: SUB SI,2
ADD AX,TABLE[SI]
LOOP ADD_NEXT

25ª Questão
Repita o exercício anterior para a rotina abaixo. Este programa controla um canal de
comunicação assíncrono.

USART_DATA EQU 0FF0H


USART_STAT EQU 0FF2H

CHAR_IN PROC NEAR


MOV DX,USART_STAT
AGAIN: IN AL,DX
AND AL,2
JZ AGAIN
MOV DX,USART_DATA
IN AL,DX
RET
CHAR_IN ENDP

26ª Questão
O programa abaixo é um gerador de números aleatórios. Repita o exercício anterior
para este programa.

SAVE DW ? ; o "offset" de SAVE é 000AH


RANDOM PROC NEAR
MOV CX,2053
MUL CX
ADD AX,13849

última revisão Ago/2007 50


Linguagem Assembly Família Intel

MOV SAVE,AX
SUB DX,DX
MOV CX,6
DIV CX
MOV AX,DX
RET

RANDOM ENDP

27ª Questão:
O programa abaixo calcula o fatorial de um número maior do que zero colocado em
AX. Estime o tempo de execução da rotina abaixo em função do conteúdo de AX.
Admita que os acessos a dados estão afetados de ciclos de espera, conforme a
tabela abaixo:
leitura escrita
dados na pilha 1 2
dados fora da pilha 3 4

FATORIAL PROC NEAR


CMP AX,1
JE ACABOU
PUSH AX
DEC AX
CALL FATORIAL
POP BX
MUL BX
ACABOU: RET
FATORIAL ENDP

28ª Questão:
Nas subrotinas descritas abaixo, ALFA é uma constante. Estime o tempo de
execução das subrotinas em função de ALFA, desde o instante em que inicia sua
execução até o momento em que ocorre o retorno ao programa principal. Observe se
vale a pena utilizar as instruções de manipulação de string neste caso, do ponto de
vista do desempenho. Admita que cada acesso de leitura ou escrita a dados é
afetado respectivamente por um ou dois ciclos de espera.

VERSAO_1 PROC VERSAO_2 PROC NEAR


NEAR
MOV CX,ALFA
MOV CLD
CX,ALFA
SUB BL,BL SUB BL,BL
VOLTA: XOR BL,[SI] VOLTA: LODSB
última revisão Ago/2007 51
Linguagem Assembly Família Intel

INC SI XOR BL,AL


DEC CX LOOP VOLTA
JNZ VOLTA RET
RET
VERSAO_1 ENDP VERSAO_2 ENDP

29ª Questão:
Escreva seqüências de instruções equivalentes a cada uma das instruções de
manipulação de string. Determine em seguida o tempo de execução da instrução de
"string" e da seqüência equivalente. Compare os resultados.

última revisão Ago/2007 52

Você também pode gostar