Buscar

8086 MICROPROCESSADOR ASSSEMBLY

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

8086 
MICROPROCESSADOR 
Linguagem Assembly 
 
 
 
 
 
 
 
 
 
 
Maria João Nicolau 
Universidade do Minho – Portugal 
http://piano.dsi.uminho.pt/~joao/Computacao2/node16.html 
Pesquisa realizada pelos alunos: 
 
CARLOS AUGUSTO PETRINI 
 PAULO TEODORO PINTO JÚNIOR 
 VALDECIR REINALDO TASCA 
 
3° PD – Noturno 
Faculdade de Tecnologia de Americana 
Agosto/2002 
O MICROPROCESSADOR 8086 
Introdução 
Os computadores podem ser divididos em diferentes tipos tendo em conta o seu tamanho e capacidade. 
Os maiores e mais poderosos são os chamados mainframes e chegam por vezes a ocupar uma sala. No 
outro extremo estão os chamados microcomputadores, usados habitualmente como computadores 
pessoais. 
Os microprocessadores mais usados no fabrico de microcomputadores são os produzidos pela Intel, 
que constituem já uma família numerosa. O 8086 foi o primeiro membro desta família e apesar de ter 
sido há muito ultrapassado pelos seus sucessores continua a ser utilizado como um ponto de partida 
para o estudo dos microprocessadores. 
Durante as próximas sessões de trabalho vamos debruçar-nos sobre variados aspectos dos 
microprocessadores, utilizando o 8086 como caso prático. O objetivo deste texto de apoio é somente 
fazer a revisão de alguns conceitos já estudados e introduzir o microprocessador 8086, a sua 
arquitetura, registros, modelo de programação, etc. 
Arquitetura dos microcomputadores - Revisões 
Um microcomputador é constituído pelos seguintes componentes: 
? O CPU (Central Processing Unit), que controla todo o funcionamento do computador. O CPU 
vai buscar instruções à memória, descodifica-as e executa-as. 
? A memória, onde se guarda instruções e dados. 
? A unidade de entradas/saídas, que permite ao computador receber e enviar dados para o 
exterior através de periféricos. 
Estas componentes estão ligadas entre si por conjuntos de linhas paralelas que se designam por 
barramentos. Os barramentos podem ser agrupados em três tipos: 
? Barramento de Endereços. É através deste barramento que o CPU consegue endereçar as 
posições de memória de onde quer ler ou para onde quer escrever dados ou instruções. O 
número de linhas que constitui o barramento de endereços determina a quantidade de memória 
que o CPU consegue endereçar. Por exemplo, um CPU com 16 linhas de endereços consegue 
endereçar 64 kbytes de memória (216 = 65 536). 
? Barramento de Dados. Este barramento é constituído por linhas bidirecionais e é usado pelo 
CPU para ler ou escrever dados na memória ou nas várias portas que constituem a unidade de 
entradas/saídas. 
? Barramento de Controle. É através deste barramento que o CPU envia os sinais que lhe 
permitem ler e escrever em posições de memória e nas várias portas de entrada/saída. 
Exemplos típicos de sinais que o CPU envia através do barramento de controle são os sinais: 
memory read, memory write, I/O read e I/O write. 
 
 
A família de microprocessadores 8086 
O microprocessador 8086 da Intel é um microprocessador de 16 bits que é suposto ser usado como 
CPU num microcomputador. Quando se diz que é um processador de 16 bits, quer-se dizer que a sua 
unidade lógica e aritmética, os seus registros internos, e a maior parte das suas instruções foram 
concebidos para trabalhar com palavras de 16 bits. Além disso o 8086 tem um barramento de dados de 
16 bits, ou seja, pode ler e escrever na memória ou nas portas 16 bits de uma só vez. O barramento de 
endereços é de 20 bits, ou seja o 8086 consegue endereçar 1 Mb (220) posições de memória. Cada uma 
destas posições de memória é ocupada por um byte. 
 
Arquitetura 
O 8086 está dividido em duas unidades distintas: a BIU (Bus Interface Unit) e a EU (Execution Unit). 
A BIU envia endereços para o barramento de endereços, lê instruções da memória, lê e escreve dados 
nas portas e na memória, etc. Por outras palavras é a unidade responsável por todas as transferências 
de dados e endereços através dos barramentos. A EU diz à BIU onde é que há de ir buscar instruções 
ou dados, descodifica as instruções e executa-as. 
 
Registros 
No total este processador é composto por 14 registros de 16 bits cada. 
 
Registros Genéricos 
Oito desses registros são chamados registros genéricos e encontram-se divididos em dois grupos de 
quatro registros cada. O primeiro grupo contém os chamados data registers (AX, BX, CX e DX) e o 
segundo os index registers (SI e DI) e os pointer registers (BP e SP). 
Apesar de todos os registros deste microprocessador serem registros de 16 bits, os registros de dados, 
AX, BX, CX e DX podem ser utilizados como registros de oito bits. Utilizamos para isso o byte mais 
significativo e o byte menos significativo de cada registro (AH e AL respectivamente, para o caso do 
registro AX). 
Registro das Flags 
Este registro é um registro de condição que reflete em cada um dos seus bits situações que ocorrem no 
interior do microprocessador durante as operações aritméticas e lógicas. 
 
Registros de Segmento e Deslocamento 
O endereçamento de memória é feito a partir dos registros de segmento e registros de deslocamento 
(offset). 
Como já foi referido, os registros internos do 8086 são todos de 16 bits e, como também já sabemos, 
com 16 bits conseguimos apenas endereçar 216 bytes, ou seja 64 Kbytes. Para endereçar 1 MByte foi 
necessário recorrer a uma técnica designada por segmentação e que consiste no seguinte: em vez de 
um registro, são utilizados dois registros. Um desses registros aponta para um valor de memória 
chamado base do segmento, enquanto o outro determina o deslocamento em relação a essa base num 
valor máximo de 64 Kbytes. 
Os registros que são a base do segmento são os registros de segmento e os registros que determinam o 
deslocamento do endereço em relação a essa base chamam-se registros de deslocamento. 
Existem 4 registros de segmento, são eles: 
? O CS (Code Segment) que endereça código (instruções que constituem programas); 
? O DS (Data Segment) que endereça dados (áreas de memória que contém dados); 
? o SS (Stack Segment) que endereça a zona de stack (zona de arquivo temporário do conteúdo 
de registros e variáveis); 
? o ES (Extra Segment) que endereça dados; 
Os registros de deslocamento trabalham associados aos registros de segmento. 
? O IP (Instruction Pointer) está associado ao registro CS; 
? O SP (Stack Pointer) está associado ao registro SS; 
? Associado aos registros DS e ES podem aparecer qualquer dos registros genéricos embora se 
usem com mais freqüência os index registers e os pointer registers. 
Um endereço representado a partir de dois valores de registros tem o nome de endereço lógico. O 
endereço físico ou endereço real é calculado a partir desse endereço lógico, deslocando o valor de base 
do endereço, 4 bits para a esquerda, e somando ao resultado o valor do deslocamento. 
Exercícios: 
? Qual o endereço físico associado ao endereço lógico 0020h:0056h; 
? Qual o endereço lógico associado ao endereço físico 02100h; 
 
 
Programação do 8086 - Introdução 
Introdução 
Para compreendermos a estrutura de um microcomputador e os seus circuitos internos, nada melhor do 
que recorrermos a programa desenvolvidos em linguagem Assembly, uma vez que esta linguagem está 
muito próxima dos circuitos eletrônicos da máquina. 
Como já foi referido os microcomputadores que vamos estudar são os baseados nos 
microprocessadores 8086 da Intel. Todos os modelos que se seguiram ao 8086 são compatíveis com 
este em instruções e modo de funcionamento. Os microprocessadores posteriores ao 8086 (286, 386, 
496 e Pentium) podem trabalhar em modo real ou em modo protegido. O modo real, onde o 
endereçamentoda memória é feito usando a segmentação, é totalmente compatível com o modo de 
funcionamento do 8086. 
O objetivo desta sessão de trabalho é introduzir a linguagem Assembly do 8086. Os registros do 8086 
e a forma como este endereça a memória foram introduzidos na sessão de trabalho anterior. No fim 
desta sessão de trabalho os alunos deverão ser capazes de escrever e compreender o código de 
programas muitos simples desenvolvidos em linguagem Assembly do 8086, bem como estar 
familiarizados com as ferramentas necessárias ao desenvolvimento de programas nesta linguagem. 
Linguagens de Programação 
Para executar um programa, o microcomputador tem de ter na memória a seqüência de instruções que 
o constituem, na forma binária. Existem múltiplas linguagem de programação alternativas, mas 
basicamente ela dividem-se em três tipos. São eles: 
? Linguagem Máquina. Esta é a linguagem entendida pelo computador. Um programa em 
linguagem máquina é uma seqüência de códigos binários que correspondem às instruções que 
se pretende que o computador execute. Esta linguagem é muito pouco usada pelos 
programadores, uma vez que é muito difícil memorizar os milhares de códigos binários de 
instruções que tem um microprocessador. 
? Linguagens Assembly. Com o objetivo de tornar mais fácil a programação sem perder o 
controle do hardware muitos programadores utilizam a linguagem Assembly. A linguagem 
Assembly é constituída por um conjunto de instruções simbólicas que são mapeadas 
diretamente para linguagem máquina usando assemblers. Os assemblers traduzem, uma a 
uma, as instruções da linguagem Assembly em instruções da linguagem máquina. Uma 
instrução em Assembly corresponde a uma instrução em linguagem máquina. 
? Linguagens de alto nível. Estas linguagens estão mais próximas da linguagem natural do que 
da linguagem máquina. O trabalho de traduzir uma linguagem na outra cabe a programas 
bastante complexos que se designam por compiladores. Uma instrução numa linguagem de 
alto nível corresponde normalmente a várias instruções em linguagem máquina. 
 
A linguagem Assembly do 8086 
Para introduzir a linguagem Assembly do 8086 vamos utilizar o programa exemplo.asm. Trata-se de 
um programa completo, embora muito simples, que tem como objetivo multiplicar dois números de 16 
bits. 
DATA_HERE SEGMENT 
 MULTIPLICANDO DW 204AH 
 MULTIPLICADOR DW 382AH 
 PRODUTO DW 2 DUP(0) 
DATA_HERE ENDS 
CODE_HERE SEGMENT 
 ASSUME CS:CODE_HERE, DS:DATA_HERE 
 MOV AX, DATA_HERE 
 MOV DS, AX 
 MOV AX, MULTIPLICANDO 
 MUL MULTIPLICADOR 
 MOV PRODUTO, AX 
 MOV PRODUTO+2, DX 
 MOV AH, 4CH 
 INT 21H 
CODE_HERE ENDS 
 END 
Este programa multiplica dois números de 16 bits e dá como resultado um número de 32 bits que é 
guardado em memória. 
Instruções de inicialização 
Como em todas as linguagens de programação existem na linguagem Assembly uma série de 
instruções que é necessário incluir antes de se começar a escrever o programa propriamente dito. A 
essas instruções vamos chamar instruções de inicialização. A finalidade destas instruções é efetuar 
uma série de inicializações que permitem a correta execução do programa. 
As diretivas SEGMENT e ENDS e a diretiva ASSUME 
As diretivas SEGMENT e ENDS são usadas para identificar o grupo de dados ou de instruções que 
pretendemos que façam parte do mesmo segmento. Todas os dados ou instruções que aparecem entre a 
diretiva SEGMENT e a diretiva ENDS fazem parte do mesmo segmento lógico. 
Um programa em linguagem Assembly do 8086 pode ter vários segmentos de dados e vários 
segmentos de código, no entanto no momento em que o executamos só trabalha com 4 segmentos 
físicos. São eles o code segment, o data segment, o stack segment e o extra segment. A diretiva 
ASSUME indica ao assembler quais dos segmentos lógicos correspondem a cada um destes 
segmentos. 
Inicialização dos registros de segmento 
Uma das inicializações que precisa de ser sempre feita é a inicialização dos registros de segmento. 
Estes registros precisam de ser carregados com os endereços de memória onde queremos que os 
segmentos comecem. 
Como já foi referido a diretiva ASSUME diz ao assembler quais os nomes dos segmentos lógicos que 
se pretende usar como code segment, data segment, stack segment e extra segment. Com excepção do 
registro CS, todos os outros registros de segmento necessitam de ser inicializados. No programa 
exemplo.asm as instruções MOV AX,DATA_HERE e MOV DS,AX servem para inicializar o 
segmento de dados. 
Nomes para os dados 
Os programas trabalham com três tipos de dados: constantes, variáveis e endereços. O Assembly do 
8086 permite dar nomes a qualquer um destes tipos de dados, o que permite referenciá-los de forma 
muito mais fácil. 
As diretivas DB, DW e DD são usadas para atribuir nomes a variáveis. A diretiva DB depois de um 
nome significa que esse nome corresponde a uma variável do tipo byte (8 bits), a diretiva DW significa 
que o nome corresponde a uma variável do tipo word (16 bits) e a diretiva DD significa que o nome 
corresponde a uma variável do tipo double word (32 bits). 
No programa exemplo.asm a instrução MULTIPLICANDO DW 204AH declara uma variável do tipo 
word e inicializa-a com 204AH. A instrução MULTIPLICADOR DW 3B2AH declara uma variável 
também do tipo word e inicializa-a com o valor 3B2AH. A declaração PRODUTO DW 2 DUP(0) 
guarda espaço para duas words em memória, dá ao endereço do inicio da primeira word o nome 
PRODUTO, e inicializa as duas words com o valor zero. 
Como acessar os dados 
As diferentes formas que o microprocessador utiliza para acessar aos dados chamam-se modos de 
endereçamento. Na linguagem Assembly o modo de endereçamento usado é indicado na própria 
instrução. No caso do Assembly do 8086 podemos encontrar os seguintes modos de endereçamento. 
? Modo de endereçamento imediato. É possível que num programa haja necessidade de carregar 
um número num registro. Por exemplo, no programa exemplo.asm uma das primeiras 
instruções tem como objetivo carregar o número 204AH no registro AX. Para fazer isso 
poderíamos ter usado a instrução MOV AX, 204AH. Este modo de endereçamento chama-se 
modo endereçamento imediato porque o número que vai ser carregado em AX é posto nas 
duas posições de memória imediatamente a seguir ao código binário da instrução MOV. 
? Modo de endereçamento por registros. Acontece sempre que um registro é usado como 
operando fonte numa dada instrução, por exemplo, na instrução MOV DS,AX; 
? Modo de endereçamento direto. Como já foi referido para aceder a dados que estão em 
memória o 8086 necessita de especificar um endereço de 20 bits. Esse endereço é 
normalmente obtido a partir do DS e do endereço efetivo. O endereço efetivo é um número de 
16 bits que nos dá um deslocamento em relação à base do segmento de dados. A instrução 
MOV AX, MULITPLICANDO é um exemplo de endereçamento direto. Esta instrução é 
equivalente à instrução MOV AX, [0000]. O deslocamento da variável MULTIPLICANDO em 
relação ao inicio do segmento de dados é 0 uma vez que esta variável é a primeira a ser 
declarada. Para calcularmos o endereço físico do dado que queremos somar a AX basta-nos 
fazer um shift de 4 bits ao DS e somar o endereço efetivo, neste caso o 0, ao resultado. 
? Modo de endereçamento indireto. Acontece quando o endereço efetivo está contido num 
registro em vez de aparecer diretamente na instrução, por exemplo na instrução MOV AX, 
[BX]. 
 
Instruções para transferência de dados 
? MOV 
A instrução MOV do 8086 tem o seguinte formato: MOV <destino>, <fonte> 
Quando executada, esta instrução, copia uma palavra ou um byte da localização especificada por 
<fonte> paraa localização especificada por <destino>. A fonte pode ser um número, um 
registro, ou uma posição de memória. O destino pode ser um registro ou uma posição de 
memória. A fonte e o destino não podem ser simultaneamente posições de memória. 
Operações aritméticas 
? ADD 
A instrução ADD do 8086 tem o seguinte formato: ADD <destino> <fonte> 
Quando executada esta instrução soma dois números, o <destino> e a <fonte>. O resultado é 
colocado no <destino>. 
? SUB 
A instrução SUB do 8086 tem o seguinte formato: SUB <destino> <fonte> 
Quando executada esta instrução subtrai o número especificado por <fonte> ao número 
especificado por <destino> e guarda o resultado em <destino>. 
? MUL 
A instrução MUL do 8086 tem o seguinte formato: MUL <fonte> 
Quando executada esta instrução multiplica um número contido em AL ou em AX pelo número 
especificado por <fonte>. Quando o número especificado por <fonte> é um número de oito bits 
esse número é multiplicado pelo conteúdo de AL e o resultado é colocado no registro AX. 
Quando o número especificado por fonte é um número de 16 bits esse número é multiplicado 
pelo conteúdo de AX e o resultado (produto) é colocado no registro DX (a palavra mais 
significativa do produto) e em AX (a palavra menos significativa do produto). 
No programa exemplo.asm, a instrução MUL MULTIPLICADOR, multiplica o conteúdo do 
registro AX pelo número 382AH. 
? DIV 
A instrução DIV do 8086 tem o seguinte formato: DIV <fonte> 
Quando executada esta instrução divide uma double word por uma word ou uma word por um 
byte. Se o número especificado por <fonte> for um byte, a instrução DIV divide o conteúdo do 
registro AX por esse número. Depois da divisão concluída, o quociente é colocado no registro 
AL e o resto no registro AH. Se o número especificado por <fonte> for uma word a instrução 
DIV forma o dividendo com os registros DX (word mais significativa do dividendo) e AX (word 
menos significativa do dividendo). Depois da divisão concluída, o quociente é colocado no 
registro AX e o resto no registro DX. 
Operações lógicas 
? AND 
A instrução AND do 8086 tem o seguinte formato: AND <destino> <fonte> 
Quando executada esta instrução faz o e lógico bit a bit da <fonte> e do <destino>. O resultado 
da operação é colocado no <destino> e a <fonte> não sofre alterações. 
? OR 
A instrução OR do 8086 tem o seguinte formato: OR <destino> <fonte> 
Quando executada esta instrução faz o ou lógico bit a bit da <fonte> e do <destino>. O resultado 
da operação é colocado no <destino> e a <fonte> não sofre alterações. 
? NOT 
A instrução NOT do 8086 tem o seguinte formato: A NOT <destino> 
Quando executada esta instrução inverte todos os bits do <destino> e guarda o resultado no 
próprio <destino>. 
Outras operações 
? ROL e ROR 
As instruções ROL e ROR do 8086 têm o seguinte formato: ROL <destino>, <count> ROR 
<destino>, <count> 
Estas instruções quando executadas rodam todos os bits de um byte ou de uma word (<destino>) 
um determinado número de posições para a esquerda (ROL) ou para a direita (ROR). O número 
de posições a rodar é dado por <count> e o resultado é colocado em destino. 
? SHL e SHR 
As instruções SHL e SHR do 8086 têm o seguinte formato: SHL <destino>, <count> SHR 
<destino>, <count> 
Estas instruções quando executadas afastam todos os bits de byte ou de uma word (<destino>) 
um determinado número de posições para a esquerda (SHL) ou para a direita (SHR) inserindo 
zeros no lugar dos bits que foram afastados. O número de posições a afastar é dado por <count> 
e o resultado é colocado em destino. 
A diretiva END 
A diretiva END serve para dizer ao assembler para parar de ler o programa uma vez que ele terminou. 
Quaisquer instruções depois desta diretiva são ignoradas. 
 
Ferramentas necessárias para programar em Assembly 
Como as todas as outras linguagens também a linguagem Assembly pressupõe o domínio de algumas 
ferramentas. São elas: um editor de texto, um assembler, um linker e um debugger. 
 
Editor 
A primeira coisa a fazer é escrever o programa fonte usando um editor de texto. O formato do 
programa deve ser ASCII puro para que os dígitos de controle que os processadores de texto 
geralmente juntam ao documento não apareçam no programa. 
Depois de escrito o programa deve ser gravado em disco com a extensão ASM. 
 
Assembler 
O assembler é um programa que traduz as instruções da linguagem Assembly nos seus códigos binários 
correspondentes. Faz normalmente duas passagens pelo código fonte do programa. Na primeira 
passagem calcula os deslocamentos dos vários itens de dados e dos labels, construindo a tabela se 
símbolos. Na segunda passagem o assembler produz o código binário para cada instrução e insere os 
deslocamentos que calculou durante a primeira passagem. 
O assembler gera os seguintes ficheiros. 
? Um ficheiro com a extensão OBJ que contém os códigos binários das instruções e informação 
acerca dos seus endereços. É este ficheiro que depois de ser processado pelo linker é carregado 
em memória para ser executado. 
? O outro ficheiro produzido pelo assembler tem a extensão LST e é utilizado pelo programador 
para testar e diagnosticar problemas no programa. Este ficheiro contém o código Assembly e 
os códigos binários e deslocamentos de cada instrução. Para que o TASM (Turbo Assembler) 
gere este ficheiro é necessário usar o switch /l. 
 
 
Linker 
O linker é um programa que além de preparar o código objeto para ser carregado em memória e 
executado, pode juntar partes de código num único programa executável. 
O linker gera um ficheiro com a extensão EXE que contém o código máquina necessário à execução do 
programa. 
Existem dois tipos de ficheiros executáveis, os ficheiros EXE e os ficheiros COM. Os ficheiros EXE 
podem utilizar mais do que um segmento de memória na sua execução. Os ficheiros COM, são 
ficheiros de dimensões mais reduzidas que só utilizam um segmento de memória, e começam 
geralmente num endereço cujo deslocamento é 0100H. Os ficheiros COM podem ser obtidos a partir 
de ficheiros EXE utilizando para isso o programa EXE2BIN. 
 
Debugger 
O debugger é um programa que permite carregar um programa executável em memória, e executá-lo 
testando-o. É possível examinar e até modificar conteúdo dos registros e posições de memória á 
medida que o programa é executado. Por exemplo se se parar no fim de executar cada instrução, 
consegue-se analisar todas as alterações nos valores dos registros ou em posições de memória pelas 
quais o programa em execução é responsável. Em vez de parar no fim da execução de cada instrução 
podem inserir-se breakpoints nalguns pontos do programa. Nesse caso a execução só pára quando 
encontra um breakpoint. 
Para executar um programa usando o Turbo Debugger é necessário usar os switchs /zi quando usa o 
TASM e o switch /v quando usa o TLINK. 
 
Exercícios 
? Descreva o funcionamento e o resultado de cada uma das seguintes instruções: 
 
MOX AX, BX 
MOV CL, 37H 
MOV CX, [246BH] 
MOV CX, 246BH 
MOV AX, [BX] 
ADD AL, DH 
MUL BX 
DIV BL 
SUB AX, DX 
OR CL, BL 
NOT AH 
ROL BX, 1 
ROR BX, CL 
AND AL, 0FH 
Descubra erros nas seguintes instruções: 
MOX BH, AX 
MOV DX, CL 
ADD AL, 2073H 
MOV 7632H, CX 
Explique o funcionamento e o resultado dos seguintes grupos de instruções: 
ADD BL, AL 
MOV [0004], BL 
MOV CL, 04 
ROR DI, CL 
MOV BX, 0004H 
MOV AL, [BX] 
SUB AL, CL 
INC BX 
MOV [BX], AL 
 
 
? Escreva um programa em Assembly do 8086 que soma dois números e guarda esse resultado 
em memória. Utilize o programa desenvolvido para somar os números +115 e +79 e interprete 
o resultado.? Escreva um programa, em linguagem Assembly do 8086, que calcula a média entre dois 
números. 
? Escreva um programa em Assembly do 8086 que converte dois dígitos decimais representados 
em ASCII para o código packet BCD. 
 
 
 
Linguagem Assembly do 8086 - Implementação de 
Expressões condicionais 
Introdução e Objetivos 
Quando temos em mão um problema de programação para resolver, o primeiro passo a dar é pensar no 
que queremos de fato que o programa faça e de que forma queremos que o faça. Como sabe, à 
seqüência de operações usadas para resolver um problema no contexto da programação chama-se 
algoritmo. Depois do algoritmo escrito, o próximo passo é traduzi-lo para a linguagem de 
programação em causa, neste caso para a linguagem Assembly do 8086. 
Os programas que implementamos na sessão de trabalho anterior são simples seqüências de instruções. 
No entanto, muitos dos algoritmos exigem funcionalidades mais complicadas, como por exemplo, 
escolher entre duas ou mais seqüências de instruções dependendo de alguma condição ou repetir a 
mesma seqüência de instruções até que uma dada condição ocorra. 
O objetivo desta sessão de trabalho é mostrar aos alunos como se implementam algoritmos com 
expressões condicionais na linguagem Assembly do 8086. 
 
Saltos, Flags e Saltos Condicionais 
Como já foi dito numa sessão anterior, o registro CS (Code Segment Register) contém os 16 bits mais 
significativos do endereço físico onde começa o segmento de código. Esse segmento contém os 
códigos binários das instruções que constituem o programa que está a ser executado. O registro IP 
(Instruction Pointer Register) referencia dentro do segmento de código qual é a próxima instrução a ser 
executada, ou seja, o seu valor representa o deslocamento em relação ao CS que tem a próxima 
instrução. 
Se o programa em causa for uma simples seqüência de instruções o endereço da próxima instrução a 
executar é sempre o imediatamente a seguir à instrução anterior. Isto já não é assim se o programa 
contiver saltos (jumps). 
As instruções JMP são usadas para indicar ao processador que a próxima instrução a executar não é a 
que está imediatamente a seguir mas sim outra. Existem jumps condicionais que ocorrem se uma 
determinada condição se verificar e jumps incondicionais que ocorrem sempre, sem estarem sujeitos a 
qualquer condição. 
O 8086 calcula o endereço físico da próxima instrução a executar adicionando o deslocamento contido 
no registro IP ao endereço de base representado pelo número contido no registro CS. Quando o 8086 
executa a instrução JMP, carrega um novo número (deslocamento) no registro IP e por vezes também 
um novo número no registro CS. 
? JMP 
A instrução JMP do Assembly do 8086 tem o seguinte formato: JMP <destino> 
Se o destino está num segmento de código diferente do da própria instrução então é necessário 
alterar não só o valor do registro IP, mas também o valor do registro CS. Neste caso, diz-se que 
estamos perante um far jump. Se, pelo contrário o destino está no mesmo segmento diz-se que 
estamos perante um near jump. 
Os jumps podem também ser diretos ou indiretos. São diretos se o destino for um número, 
número esse que referencia um deslocamento em relação à localização atual. Nesse caso o novo 
valor do IP ficará a ser IP+<destino>. São indiretos se o destino for um registro ou localização 
de memória. Nesse caso o 8086 terá de ir buscar o conteúdo do registro ou localização de 
memória, conteúdo esse que passará a ser o novo valor do registro IP. 
No caso dos jumps diretos do tipo near o destino pode ser um número de 8 bits ou de 16 bits. No 
caso de ser um número de 16 bits pode variar desde 32767 a -32768. No caso de ser um número 
de 8 bits diz-se também que é do tipo short e referencia um deslocamento que varia entre 127 e -
128 em relação à localização atual. 
O 8086 tem seis flags condicionais: a carry flag (CF), a parity flag (PF), a auxiliary parity flag (AF), a 
zero flag (ZF), a sign flag (SF) e a overflow flag (OF). 
As várias instruções do Assembly 8086 alteram os valores das várias flags, por exemplo, quando se 
somam dois números de 16 bits e o resultado não cabe em 16 bits a CF fica com o valor 1. 
Como já foi referido um jump condicional está sujeito a uma determinada condição. A instruções que 
especificam jumps condicionais analisam o conteúdo de determinadas flags para decidir se o jump se faz 
ou não, 
Todos os jumps condicionais são do tipo short, isto significa que o destino tem de especificar um 
endereço dentro do mesmo segmento da instrução JMP e além disso o deslocamento em relação à 
localização atual varia apenas de 127 a -128. 
Estas instruções aparecem normalmente depois de instruções de operações aritméticas e lógicas (que 
alteram os valores das flags), e quase sempre depois da instrução CMP. 
? CMP 
A instrução CMP tem o seguinte formato: CMP <destino>, <fonte> 
Quando executada esta instrução compara o byte ou word especificada por <fonte> pelo byte ou 
word especificada por <destino>. A fonte pode ser um número, um registro, ou uma posição de 
memória. O destino pode ser um registro ou uma posição de memória. A fonte e o destino não 
podem ser simultaneamente posições de memória. 
Esta instrução é muitas vezes usada com as seguintes instruções que implementam jumps condicionais: 
? JA/JNBE - Jump if Above ou Jump if Not Below or Equal 
? JAE/JNB - Jump if Above or Equal ou Jump if Not Below 
? JB/JNAE - Jump if Below ou Jump if Not Above or Equal 
? JBE/JNA - Jump if Below or Equal ou Jump if Not Above 
? JG/JNLE - Jump if Greater ou Jump if Not Less or Equal 
? JAE/JNB - Jump if Greater or Equal ou Jump if Not Less 
? JB/JNAE - Jump if Less ou Jump if Not Greater or Equal 
? JBE/JNA - Jump if Less or Equal ou Jump if Not Greater 
? JE - Jump if Equal 
? JNE - Jump if Not Equal 
Os termos above e below utilizam-se quando queremos comparar números sem sinal. Os termos greater 
e less utilizam-se quando queremos comparar números com sinal. 
Além destas instruções, existem outras que fazem depender os jumps diretamente dos valores das flags. 
São elas: 
? JC - Jump if Carry (CF=1) 
? JNC - Jump if Not Carry (CF=0) 
? JZ - Jump if Zero (ZF=1) 
? JNZ - Jump if Not Zero (ZF=0) 
? JO - Jump if Overflow (OF=1) 
? JNO - Jump if Not Overflow (OF=0) 
? JP - Jump if Parity (PF=1) 
? JNP - Jump if Not Parity (PF=0) 
? JS - Jump if Sign (SF=1) 
? JNS - Jump if Not Sign (SF=0) 
 
Expressões Condicionais 
If-Then 
A expressão algorítmica If-Then tem o seguinte formato: 
if <condicao> then 
accao 
accao 
 ... 
endif 
 
Esta expressão quer dizer que, se a <condição> se verificar a seqüência de ações que está entre o then 
e o endif deve ser executada. 
Na linguagem Assembly do 8086 esta expressão é implementada com um jump condicional. 
Por exemplo, o algoritmo: 
if AX=BX then 
AX=AX+2 
CL=7 
 ... 
Seria traduzido para linguagem Assembly do 8086 da seguinte forma: 
CMP AX, BX 
JNE FIMSE 
ADD AX, 0002H 
FIMSE: ADD CL, 07H 
 ... 
Outra hipótese seria: 
CMP AX, BX 
JE ENTAO 
JMP FIMSE 
ENTAO: ADD AX, 0002H 
FIMSE: ADD CL, 07H 
 ... 
Se a seqüência de instruções entre o then e o endif contiver muitas instruções a segunda hipótese é a 
mais aconselhável, uma vez que, como já foi referido, os jumps condicionais são do tipo short. 
If-Then-Else 
A expressão algorítmica if-then-else tem o seguinte formato: 
if <condicao> then 
accao 
accao 
 ... 
else 
accao 
accao 
 ... 
endif 
Esta expressão quer dizer que, se a <condição> se verificara seqüência de ações que está entre o then 
e o else deve ser executada. Caso contrário a seqüência de ações que está entre o else e o endif deve ser 
executada. 
Na linguagem Assembly do 8086 esta expressão é implementada com um jump condicional e um jump 
incondicional. 
Por exemplo, o algoritmo: 
if AX=BX then 
AX=AX+2 
else 
AX=AX-2 
CL=7 
 ... 
Seria assim traduzido para linguagem Assembly: 
CMP AX, BX 
JNE SENAO 
ADD AX, 0002H 
JMP FIMSE 
SENAO: SUB AX, 0002H 
FIMSE: MOV CL, 07H 
 ... 
 
 
Exercícios: 
? Descubra erros nas seguintes instruções ou grupos de instruções: 
? JMP BL 
? JNZ [BX] 
? CNTDOWN: MOV BL, 72H 
DEC BL 
JNZ CNTDOWN 
? Escreva um programa em Assembly do 8086 que encontra o maior de dois números e 
armazena-o numa variável em memória. 
? Escreva um programa em Assembly do 8086 que converta um byte (constituído por dois 
dígitos Hexadecimais) em dois códigos ASCII (os dois códigos ASCII correspondentes aos 
dois dígitos Hexadecimais). 
 
 
Linguagem Assembly do 8086 - Implementação de 
Ciclos e Arrays 
Introdução e Objetivos 
A resolução de problemas de programação passa, muitas vezes, pela repetição da mesma seqüência de 
ações. Existem várias expressões algorítmicas que têm como objetivo a implementação de ciclos, ou 
seja a repetição da mesma seqüência de instruções até que determinada condição se verifique. 
Exemplos de expressões que implementam ciclos são as expressões While-Do, Repeat-Until e For-To-
Do. Estas expressões são particularmente úteis quando se pretende repetir a mesma operação sobre um 
conjunto de items de dados. 
O objetivo desta sessão de trabalho é mostrar aos alunos como se implementam algoritmos com ciclos 
na linguagem Assembly do 8086 
Ciclos 
While-Do 
A expressão algorítmica While-Do tem o seguinte formato: 
while <condição> do 
accao 
accao 
 ... 
endwhile 
Esta expressão quer dizer que, enquanto a <condição> se verificar, a seqüência de ações que está entre 
o do e o endwhile deve ser repetida. De notar que a <condição> é verificada antes de qualquer das 
ações ser levada a cabo. 
Na linguagem Assembly do 8086 esta expressão é implementada com um jump condicional e dois 
jumps incondicionais. 
Por exemplo, o algoritmo: 
while AX<BX do 
AX=AX+1 
CL=7 
 ... 
Seria traduzido para linguagem Assembly do 8086 da seguinte forma: 
ENQUANTO: CMP AX, BX 
 JB FAZER 
 JMP FIMENQUANTO 
FAZER: INC AX 
 JMP ENQUANTO 
FIMENQUANTO: ADD CL, 07H 
 ... 
 
Repeat-Until 
A expressão algorítmica Repeat-Until tem o seguinte formato: 
repeat 
accao 
accao 
 ... 
until <condição> 
endrepeat 
Esta expressão tem o mesmo significado que a expressão while-do com a diferença de que a condição é 
verificada apenas no fim de ser executada a seqüência de ações. Isto faz com que a seqüência de ações 
seja sempre executada uma vez, quer a condição se verifique, quer não. 
Na linguagem Assembly do 8086 esta expressão é implementada com um jump condicional. 
Por exemplo, o algoritmo: 
repeat 
AX=AX+1 
until AX<BX 
endrepeat 
CL=7 
 ... 
Seria traduzido para linguagem Assembly do 8086 da seguinte forma: 
REPETIR: INC AX 
 CMP AX, BX 
 JB REPETIR 
 ADD CL, 07H 
 ... 
Outra hipótese seria: 
REPETIR: INC AX 
 CMP AX, BX 
 JNB FIMREPETIR 
 JMP REPETIR 
FIMREPETIR: ADD CL, 07H 
 ... 
Se a seqüência de instruções a repetir (seqüência de instruções que está entre o Repeat e o Until 
contiver muitas instruções a segunda hipótese é a mais aconselhável, uma vez que, como já foi 
referido, os jumps condicionais são do tipo short. 
For-To-Do 
A expressão algorítmica For-To-Do tem o seguinte formato: 
for <count=1> to <count=n> 
accao 
accao 
 ... 
endfor 
Esta expressão é uma variante da expressão while-do onde se sabe à partida quantas vezes queremos 
executar a seqüência de ações. Por cada vez que a seqüência de ações é executada a variável count é 
incrementada um valor. Quando atingir o valor count o ciclo termina. 
Na linguagem Assembly do 8086 esta expressão é implementada com a instrução LOOP. 
? LOOP 
A instrução LOOP do 8086 tem o seguinte formato: LOOP <label> 
Quando executada, esta instrução, repete uma seqüência de instruções um determinado número 
de vezes contido no registro CX. Cada vez que a instrução LOOP é executada o conteúdo do 
registro CX é decrementado um valor. Se depois de decrementado o conteúdo do registro CX 
for diferente de zero a instrução LOOP implementa um jump para o endereço referenciado por 
<label>. O <label> referencia um deslocamento em relação ao registro CS onde está a primeira 
instrução do ciclo. Esse valor tem de estar contido entre -128 e +127. 
Por exemplo, o algoritmo: 
for count=1 to count=10 
AX=AX+1 
endfor 
CL=7 
 ... 
Seria traduzido para linguagem Assembly do 8086 da seguinte forma: 
MOV CX, 0AH 
REPETIR: INC AX 
 LOOP REPETIR 
 ADD CL, 07H 
 ... 
Arrays 
Os ciclos são especialmente úteis quando se pretende implementar a mesma operação ou a mesma 
seqüência de operações para um conjunto de items de dados guardados em posições contíguas de 
memória. Um conjunto de items de dados do mesmo tipo armazenados em posições contíguas de 
memória chama-se um array. 
Na linguagem Assembly do 8086 um array de 10 bytes podia ser declarado e inicializado da seguinte 
forma: 
DATA SEGMENT 
 ... 
ARRAY DB 00H, 10H, 20H, 30H, 40H, 50H, 60H, 70H, 80H, 90H 
 ... 
DATA ENDS 
Neste caso deu-se ao array o nome ARRAY. Quando o programa for carregado em memória para ser 
executado, dez bytes do segmento de dados vão ser carregados com os valores 00H, 10H, 20H, 30H, 
40H, 50H, 60H, 70H, 80H e 90H, em posições contíguas de memória. 
Para aceder aos items de dados que constituem o array pode-se utilizar endereçamento direto ou 
indireto. Por exemplo, para mover para o registro AL o quinto elemento do array, utilizando o 
endereçamento direto, poderíamos usar a seguinte instrução: 
MOV AX, ARRAY+0005H 
Utilizando o endereçamento indireto seria necessário a seguinte seqüência de instruções: 
LEA BX, ARRAY+05H 
MOV AL, [BX] 
A instrução LEA é utilizada para ir buscar o endereço efetivo (deslocamento em relação ao registro de 
segmento) da variável ARRAY. 
? LEA 
A instrução LEA do 8086 tem o seguinte formato: LEA <destino>, <fonte> 
Quando executada, esta instrução, carrega o deslocamento da posição de memória especificada 
por <fonte> no registro de 16 bits especificado por <destino>. 
Também é possível indexar os vários elementos de um array. Neste caso a seqüência de instruções 
seria: 
MOV BX, 05H 
MOV AL, ARRAY[BX] 
Aqui, o endereço efetivo do elemento do array é calculado somando o endereço do ARRAY e o 
conteúdo de BX. 
Exercícios 
? Implemente um programa em Assembly do 8086 que calcule a média de 4 bytes armazenados 
num array em memória. 
? Altere o programa anterior para que este seja capaz de calcular a média de qualquer número de 
bytes armazenados em memória num array, supondo que a dimensão do array está guardada na 
primeira posição do array. 
 
 
Linguagem Assembly do 8086 - Procedimentos 
Introdução e Objetivos 
Na sessão de trabalho anterior estudamos a implementaçãode ciclos usando a linguagem Assembly do 
8086. Um ciclo é a repetição da mesma seqüência de ações até que determinada condição se verifique. 
Por vezes necessitamos de repetir a mesma seqüência de ações não no mesmo ponto, mas sim em 
diferentes pontos de um programa. Para evitar escrever a mesma seqüência de instruções de cada vez 
que necessitamos dela podemos escrevê-la como se fosse um “sub-programa” separado do programa 
principal e, nesse caso, sempre que precisarmos da seqüência de instruções nele contidas, invocamo-
lo. Estes “sub-programas” designam-se habitualmente por procedimentos. 
O objetivo desta sessão de trabalho é mostrar aos alunos como se implementam programas com 
procedimentos usando a linguagem Assembly do 8086. 
Escrever e invocar procedimentos 
Para delimitar o conjunto de instruções que constituem um procedimento utilizam-se as diretivas 
PROC e ENDP. Se, em conjunto com cada uma destas diretivas usarmos um nome (label), podemos 
invocar o procedimento usando esse label. A diretiva PROC indica o inicio do procedimento e a 
diretiva ENDP o fim. 
Suponhamos que pretendíamos implementar um procedimento para calcular a média entre dois bytes. 
As fronteiras do procedimento poderão ser estabelecidas da seguinte forma: 
MEDIA PROC NEAR 
 <conjunto de instruções que constituem o procedimento MEDIA> 
MEDIA ENDP 
Sempre que precisamos de executar a seqüência de instruções contida num procedimento usamos a 
instrução CALL. É esta instrução que indica ao processador qual o endereço da primeira instrução do 
procedimento. A instrução RET tem que aparecer no fim do procedimento para indicar ao processador 
que deve voltar ao programa chamador e executar a instrução que está imediatamente a seguir à 
instrução CALL. 
? CALL 
A instrução CALL do 8086 tem o seguinte formato: CALL <destino> 
Quando executada, esta instrução, executa duas operações. Primeiro armazena o endereço da 
instrução que está imediatamente a seguir à instrução CALL. Este endereço é chamado o 
endereço de retorno porque é o endereço para o qual a execução volta depois do procedimento 
ser executado. Se a instrução CALL invoca um procedimento dentro do mesmo segmento de 
código então diz-se que estamos perante um call do tipo near. Nesse caso só é necessário 
armazenar o conteúdo do registro IP, uma vez que o valor do registro CS se mantém inalterado. 
Se a instrução CALL invoca um procedimento noutro segmento de código então é necessário 
armazenar o conteúdo do registro CS e do registro IP. A segunda operação da instrução CALL 
consiste em alterar o conteúdo do registro IP, e por vezes também o do registro CS, em função 
do <destino>. O novo par CS e IP deverá apontar para a primeira instrução do procedimento. 
A maior parte dos programas invoca os procedimentos usando o próprio nome (label). Neste 
caso diz-se que estamos perante um call direto. No entanto à semelhança do que acontece com a 
instrução JMP, pode haver calls diretos e indiretos e calls intra ou inter-segmento. 
? RET 
A instrução RET do 8086 não tem operandos. 
Como já foi dito, quando é executada a instrução CALL, o endereço de retorno é armazenado. 
Caso se trate de um call do tipo near esse endereço é constituído apenas pelo valor do IP. A 
instrução RET, quando executada, vai buscar esse valor e carrega-o de novo no registro IP. 
Escrever e invocar procedimentos 
Para delimitar o conjunto de instruções que constituem um procedimento utilizam-se as diretivas 
PROC e ENDP. Se, em conjunto com cada uma destas diretivas usarmos um nome (label), podemos 
invocar o procedimento usando esse label. A directiva PROC indica o inicio do procedimento e a 
diretiva ENDP o fim. 
Suponhamos que pretendíamos implementar um procedimento para calcular a média entre dois bytes. 
As fronteiras do procedimento poderão ser estabelecidas da seguinte forma: 
MEDIA PROC NEAR 
 <conjunto de instruções que constituem o procedimento MEDIA> 
MEDIA ENDP 
Sempre que precisamos de executar a seqüência de instruções contida num procedimento usamos a 
instrução CALL. É esta instrução que indica ao processador qual o endereço da primeira instrução do 
procedimento. A instrução RET tem que aparecer no fim do procedimento para indicar ao processador 
que deve voltar ao programa chamador e executar a instrução que está imediatamente a seguir à 
instrução CALL. 
? CALL 
A instrução CALL do 8086 tem o seguinte formato: CALL <destino> 
Quando executada, esta instrução, executa duas operações. Primeiro armazena o endereço da 
instrução que está imediatamente a seguir à instrução CALL. Este endereço é chamado o 
endereço de retorno porque é o endereço para o qual a execução volta depois do procedimento 
ser executado. Se a instrução CALL invoca um procedimento dentro do mesmo segmento de 
código então diz-se que estamos perante um call do tipo near. Nesse caso só é necessário 
armazenar o conteúdo do registro IP, uma vez que o valor do registro CS se mantém inalterado. 
Se a instrução CALL invoca um procedimento noutro segmento de código então é necessário 
armazenar o conteúdo do registro CS e do registro IP. A segunda operação da instrução CALL 
consiste em alterar o conteúdo do registro IP, e por vezes também o do registro CS, em função 
do <destino>. O novo par CS e IP deverá apontar para a primeira instrução do procedimento. 
A maior parte dos programas invoca os procedimentos usando o próprio nome (label). Neste 
caso diz-se que estamos perante um call direto. No entanto à semelhança do que acontece com a 
instrução JMP, pode haver calls diretos e indiretos e calls intra ou inter-segmento. 
? RET 
A instrução RET do 8086 não tem operandos. 
Como já foi dito, quando é executada a instrução CALL, o endereço de retorno é armazenado. 
Caso se trate de um call do tipo near esse endereço é constituído apenas pelo valor do IP. A 
instrução RET, quando executada, vai buscar esse valor e carrega-o de novo no registro IP. 
 
A stack do 8086 
Na seção anterior vimos que a instrução CALL necessitava de um local para armazenar o endereço de 
retorno, para que no fim da execução do procedimento a instrução RET o pudesse ir buscar. Esse local 
é uma zona de memória de alguma forma especial designada por stack. 
Além de servir para armazenar o endereço de retorno a stack tem também outras funções, 
nomeadamente: 
? Armazenar o conteúdo de registros que queremos preservar; 
? Passar parâmetros para/de procedimentos 
O 8086 trata a stack como um segmento de memória que à semelhança dos outros tem no máximo 
64kbytes. Existem dois registros específicos para trabalhar com a stack: o registro SS (Stack Segment) 
que à semelhança dos registros DS e CS deverá conter os 16 bits mais significativos do endereço de 
inicio da stack, e o SP (Stack Pointer) que contém o deslocamento em relação ao SS da última palavra 
escrita na stack. A posição ocupada pela última palavra escrita na stack, designa-se habitualmente por 
topo da stack e é para esta posição que aponta o registro SP. 
O SP é automaticamente decrementado dois valores antes de alguma palavra ser escrita na stack, por 
isso este registro deve ser inicializado com o tamanho da stack e não com zero. 
 
Guardar o endereço de retorno 
Já foi dito que o endereço de retorno era armazenado na stack pela instrução CALL e de lá retirado 
pela instrução RET. 
Quando a instrução CALL é executada para efetuar um call do tipo near o SP é automaticamente 
decrementado dois valores e o conteúdo do registro IP é copiado para a nova posição apontada por SP. 
Quando o processador encontra a instrução RET no fim do procedimento, copia o conteúdo da posição 
de memória apontada por SP para o registro IPe incrementa o SP dois valores. 
Por exemplo, assumindo que o valor do SS é 7000H e o valor de SP 0050H, então o endereço físico do 
topo da stack é 70050H. Depois de um call do tipo near o SP ficará com o valor 004EH e o endereço 
físico do topo da stack passaria a ser 7004EH. Quando a instrução RET for executada, o valor 
armazenado em stack (a palavra apontada por SP) voltava a ser colocado no registro IP e o registro SP 
depois de incrementado voltava ao valor 0050H. 
Para usarmos a stack num programa a primeira coisa a fazer é declará-la. Como já foi dito o 8086 trata 
a stack como outro segmento qualquer. Então para declararmos uma stack poderíamos usar a seguinte 
seqüência de instruções: 
STACK_SEG SEGMENT 
DW 40 DUP(0) 
STACK_TOP LABEL WORD 
STACK_SEG ENDS 
Esta seqüência de instruções declara uma stack com espaço para 40 words. Como as palavras são 
escritas na stack a partir do topo é conveniente ter um label que nos permita referenciá-lo. 
Como todas as instruções que escrevem palavras na stack decrementam primeiro o SP dois valores e só 
depois é que escrevem a palavra, convém inicializarmos o SP com o primeiro endereço par depois das 
40 palavras que constituem a stack. 
É também necessário inicializar o registro SS para que este aponte para o inicio da stack. Para fazer-
mos estas inicializações poderíamos usar as seguinte seqüência de instruções: 
CODE_SEG SEGMENT 
ASSUME: CS:CODE_SEG, SS:STACK_SEG 
MOV AX, STACK_SEG 
MOV SS, AX 
LEA SP, STACK_TOP 
 .......... 
CODE_SEG ENDS 
Gravar o conteúdo dos registros 
É por vezes necessário usar registros dentro do procedimento que também estão a ser usados cá fora 
no programa chamador. Para que o uso de registros dentro dos procedimentos não interfira com a sua 
utilização aqui fora usa-se a stack para guardar os seus valores originais. A idéia é escrever na stack os 
valores dos registros que são usados no procedimento antes do procedimento começar a usá-los e 
consequentemente a alterá-los, e no fim do procedimento voltar a pôr tudo como estava. 
Para isso usam-se as instruções PUSH e POP. 
? PUSH 
A instrução PUSH do 8086 tem o seguinte formato: PUSH <fonte> 
Quando executada, esta instrução, executa duas operações. Primeiro decrementa o SP dois 
valores e depois copia para a posição da stack apontada por SP o conteúdo de <fonte>. A fonte 
pode ser um registro de 16 bits ou uma palavra em memória. 
? POP 
A instrução POP do 8086 tem o seguinte formato: POP <destino> 
Quando executada, esta instrução, executa duas operações. Primeiro copia para o conteúdo de 
<destino> a palavra em stack apontada pelo registro SP e depois incrementa SP dois valores. O 
destino pode ser um registro de 16 bits ou uma palavra em memória. 
Como a stack funciona segundo o principio last-in-first-out os pops tem que ser efetuados pela ordem 
inversa pela qual foram efetuados os pushs. 
 
Passagem de argumentos de/para procedimentos 
Muitos procedimentos atuam sobre dados ou endereços e devolvem resultados ao programa chamador. 
Aos dados e endereços que passamos para/de procedimentos chamam-se parâmetros. 
Existem várias formas possíveis de passar parâmetros para e de procedimentos. São elas: 
? Através dos registros; 
? Através de posições de memória; 
? Através de apontadores para posições de memória contidos em registros; 
? Através da própria stack 
 
Passagem de parâmetros em registros 
Neste caso é necessária escolher quais os registros que vão ser usados para transportar os parâmetros 
para o procedimento. e quais os que vão ser usados para devolver os resultados ao programa 
chamador. 
Tomando como exemplo o procedimento que calcula a média entre dois bytes, vamos necessitar de 
dois registros de 8 bits (por exemplo o AL e o AH) para passar os bytes para o procedimento e um 
registro de 8 bits (por exemplo o AL) para devolver o resultado (média) ao programa chamador. 
? Implemente um procedimento que calcula a média entre dois bytes e escreva um programa que 
o invoca. Utilize registros para passagem de parâmetros e para retornar o resultado. 
 
Passagem de parâmetros em posições de memória 
É também possível pôr um procedimento a aceder a variáveis em memória através do seu próprio 
nome. Embora se use por vezes, este método não é o mais aconselhável uma vez que torna os 
procedimentos pouco flexíveis e pouco versáteis. 
? Altere o programa anterior para que o procedimento vá buscar os dados diretamente à 
memória e guarde também diretamente em memória o resultado. 
 
 
Passagem de parâmetros através de apontadores contidos 
em registros 
Para ultrapassar as limitações do método anterior usa-se muitas vezes a passagem de parâmetros 
através de apontadores para posições de memória, apontadores esses contidos em registros. 
Para isso carregam-se os registros com os endereços das posições de memória que contém as variáveis 
que queremos manipular dentro dos procedimentos, por exemplo usando a instrução LEA. Dentro do 
procedimento usamos os parêntesis retos para aceder às posições apontadas pelos registros. 
Este método é mais versátil que o anterior pois permite que se passe para o procedimento apontadores 
para quaisquer posições de memória. Além disso tanto é possível passar apontadores para dados 
simples como para arrays ou strings. 
? Altere o programa anterior de forma a que a passagem de parâmetros seja feita através de 
apontadores contidos em registros. 
 
Passagem de parâmetros utilizando a stack 
Para usar este método é necessário que algures no programa principal antes de se invocar o 
procedimento se utilize a instrução PUSH para escrever na stack os vários parâmetros. 
Dentro do procedimento as instruções vão ler os argumentos à stack de acordo com as necessidades. O 
mesmo se passa para retornar os resultados ao programa chamador, mas desta vez ao contrário. Neste 
caso são as instruções do procedimento que escrevem os resultados na stack e o programa principal 
que os vai lá buscar. 
Para usar este método é necessário fazer uma mapa da stack, para que facilmente se saiba onde é que 
estão os vários parâmetros e também para onde é que está a apontar, em cada instante, o registro SP. 
? Altere o programa anterior de forma a que a passagem de parâmetros seja feita utilizando a 
stack 
 
Exercícios 
? Suponha que um determinado programa precisava em vários pontos de converter bytes em 
packet BCD para bytes com o valor equivalente representados em código binário. 
? Escreva um procedimento em linguagem Assembly do 8086 que converte um byte em BCD 
para o seu equivalente em binário. Como parâmetro de entrada este procedimento deverá 
receber o byte em BCD a converter. Como resultado este procedimento deverá retornar o byte 
em binário convertido. Utilize os vários métodos que conhece para efetuar a passagem de 
parâmetros e para retornar os resultados. 
? Escreva um programa em Assembly do 8086 que invoca o procedimento implementado na 
alínea anterior para converter um número com 4 dígitos BCD. 
 
Referências 
1 Douglas V. Hall, Microprocessors and Interfacing - Programming and Hardware, 
McGrawHill International Editions, 1992 
2 Antônio Sampaio, HARDWARE para Profissionais, FCA - Editora de Informática, 1998

Outros materiais