<Índice> <1> <2> <3> <4> <5> <6> <7> <8> <Página Principal>




CAPÍTULO 1:
INTRODU�ÃO
Conteúdo:
1.1.O que há de novo neste material
1.2.Apresentação
1.3.Por que aprender Assembly?
                    --------------- // ---------------
1.1.O que há de novo neste material:
Após um ano da realização da primeira versão do tutorial, e através das
opiniões recebidas por e-mail, resolvemos ter por disposição todos estes
comentários e sugestões. Esperamos que através deste novo material Assembly,
as pessoas que se mostrarem interessadas possam aprender mais sobre o seu
IBM PC. Esta nova edição do tutorial inclui:
Uma seção completa sobre como usar o programa debug.
Mais exemplos de programas.
Um motor de pesquisa, para qualquer tópico ou item relacionado … esta
nova versão.
Considerável reorganização e revisão do material Assembly.
Em cada seção, há um link para o Dicionário On-line de Computação de
Dennis Howe.
 

1.2.Apresentação:
Este tutorial destina-se àquelas pessoas que nunca tiveram contato com a
Linguagem Assembly.
O tutorial está completamente focado em computadores com processadores 80x86
da família Intel, e considerando que a base da linguagem ‚ o funcionamento
dos recursos internos do processador, os exemplos descritos não são
compatíveis com qualquer outra arquitetura.
As informações estão dispostas em unidades ordenadas para permitir fácil
acesso a cada tópico, bem como uma melhor navegação pelo tutorial.
Na seção introdutória são mencionados alguns conceitos elementares sobre
computadores e a Linguagem Assembly em si.

 

1.3.Por que aprender Assembly?
A primeira razão para se trabalhar com o assembler ‚ a oportunidade de
conhecer melhor o funcionamento do seu PC, o que permite o desenvolvimento
de programas de forma mais consistente.
A segunda razão ‚ que você pode ter um controle total sobre o PC ao fazer
uso do assembler.
Uma outra razão ‚ que programas assembly são mais rápidos, menores e mais
poderosos do que os criados com outras linguagens.
Ultimamente, o assembler (montador) permite uma otimização ideal nos
programas, seja no seu tamanho ou execução.
                              


**************************************************************************************

**************************************************************************************



CAPÍTULO 2:
CONCEITOS BÁSICOS

Conteúdo:
 2.1.Descrição básica de um sistema computacional.
 2.2.Conceitos básicos da Linguagem Assembly
 2.3.Usando o programa debug
			--------------- // ---------------
                             
Esta seção tem o propósito de fazer um breve comentário a respeito dos
principais componentes de um sistema computacional, o que irá permitir ao
usuário uma melhor compreensão dos conceitos propostos no decorrer do
tutorial.
      
2.1.DESCRI�ÃO DE UM SISTEMA COMPUTACIONAL
                             
Conteúdo:
2.1.1.Processador Central
2.1.2.Memória Principal
2.1.3.Unidades de Entrada e Saída
2.1.4.Unidades de Memória Auxiliar
                                   
Sistema Computacional.
Chamamos de Sistema Computacional a completa configuração de um computador,
incluindo os periféricos e o sistema operacional.
                                   
2.1.1.Processador Central.
É também conhecido por CPU ou Unidade Central de Processamento, que por sua
vez ‚ composta pela unidade de controle e unidade de lógica e aritmética.
Sua função consiste na leitura e escrita do conteúdo das células de memória,
regular o tráfego de dados entre as células de memória e registradores
especiais, e decodificar e executar as instruções de um programa.
O processador tem uma série de células de memória usadas com frequência e,
dessa forma, são partes da CPU. Estas células são conhecidas com o nome de
registradores. Um processador de um PC possui cerca de 14 registradores.
Como os PCs tem sofrido evolução veremos que podemos manipular registradores
de 16 ou 32 bits.
A unidade de lógica e aritmética da CPU realiza as operações relacionadas ao
cálculo simbólico e numérico. Tipicamente estas unidades apenas são capazes
de realizar operações elementares, tais como: adição e subtração de dois
números inteiros, multiplicação e divisão de número inteiro, manuseio de
bits de registradores e comparação do conteúdo de dois registradores.
Computadores pessoais podem ser classificados pelo que ‚ conhecido como
tamanho da palavra, isto é, a quantidade de bits que o processador é capaz
de manusear de uma só vez.
                 
             
2.1.2.Memória Principal.
É um grupo de células, agora sendo fabricada com semi-condutores, usada para
processamentos gerais, tais como a execução de programas e o armazenamento
de informações para operações.
Cada uma das células pode conter um valor numérico e é capaz de ser
endereçada, isto é, pode ser identificada de forma singular em relação às
outras células pelo uso de um número ou endereço.
O nome genérico destas memórias ‚ Random Access Memory ou RAM. A principal
desvantagem deste tipo de memória é o fato de que seus circuitos integrados
perdem a informação que armazenavam quando a energia elétrica for
interrompida, ou seja, ela é volátil. Este foi o motivo que levou à criação
de um outro tipo de memória cuja informação não é perdida quando o sistema é
desligado. Estas memórias receberam o nome de Read Only Memory ou ROM.

 

2.1.3.Unidades de Entrada e Saída.
Para que o computador possa ser útil para nós se faz necessário que o
processador se comunique com o exterior através de interfaces que permitem a
entrada e a saída de informação entre ele e a memória. Através do uso destas
comunicações é possível introduzir informação a ser processada e mais tarde
visualizar os dados processados.
Algumas das mais comuns unidades de entrada são o teclado e o mouse. As mais
comuns unidades de saída são a tela do monitor e a impressora.

 

2.1.4.Unidades de Memória Auxiliar.  
Considerando o alto custo da memória principal e também o tamanho das
aplicações atualmente, vemos que ela é muito limitada. Logo, surgiu a
necessidade da criação de dispositivos de armazenamento práticos e
econômicos.
Estes e outros inconvenientes deram lugar às unidades de memória auxiliar,
os periféricos. As mais comuns são as fitas e os discos magnéticos.
A informação ali armazenada ser  dividida em arquivos. Um arquivo é feito de
um número variável de registros, geralmente de tamanho fixo, podendo conter
informações ou programas.
			--------------- // ---------------
                                   
2.2.CONCEITOS BÁSICOS
Conteúdo:
2.2.1.Informações nos computadores
2.2.2.Métodos de representação de dados
                                   
2.2.1.Informação no computador:
	2.2.1.1.Unidades de informação
	2.2.1.2.Sistemas numéricos
	2.2.1.3.Convertendo números binários para decimais
	2.2.1.4.Convertendo números decimais para binários
	2.2.1.5.Sistema hexadecimal
                                   
2.2.1.1.Unidades de informação
Para o PC processar a informação, é necessário que ela esteja em células
especiais, chamadas registradores.
Os registradores são grupos de 8 ou 16 flip-flops.
Um flip-flop é um dispositivo capaz de armazenar 2 níveis de voltagem, um
baixo, geralmente 0.5 volts, e outro comumente de 5 volts. O nível baixo de
energia no flip-flop ‚ interpretado como desligado ou 0, e o nível alto,
como ligado ou 1. Estes estados são geralmente conhecidos como bits, que são
a menor unidade de informação num computador.
Um grupo de 16 bits é conhecido como palavra; uma palavra pode ser dividida
em grupos de 8 bits chamados bytes, e grupos de 4 bits chamados nibbles.

2.2.1.2.Sistemas numéricos
O sistema numérico que nós usamos diariamente é o decimal, mas este sistema
não é conveniente para máquinas, pois ali as informações têm que ser
codificadas de modo a interpretar os estados da corrente (ligado-desligado);
este modo de código faz com que tenhamos que conhecer o cálculo posicional
que nos permitir  expressar um número em qualquer base onde precisarmos
dele.
É possÍvel representar um determinado número em qualquer base através da
seguinte fórmula:
        XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
                         
Onde n é a posição do dígito, iniciando da direita para a esquerda e
numerando de 0. D é o dígito sobre o qual nós operamos e B ‚ a base numérica
usada.
                             
2.2.1.3.Convertendo números binários para decimais
Quando trabalhamos com a Linguagem Assembly encontramos por acaso a
necessidade de converter números de um sistema binário, que é usado em
computadores, para o sistema decimal usado pelas pessoas.
O sistema binário ‚ baseado em apenas duas condições ou estados; estar
ligado(1), ou desligado(0), portanto sua base é dois.
Para a conversão, podemos usar a fórmula de valor posicional:
Por exemplo, se tivermos o número binário 10011, tomamos cada dígito da
direita para a esquerda e o multiplicamos pela base, elevando à potência
correspondente a sua posição relativa:
Binary:         1         1       0       0       1
Decimal:       1*2^0  + 1*2^1 + 0*2^2 + 0*2^3 + 1*2^4
            =   1     +   2   +   0   +   0   +  16  = 19 decimal.
 
2.2.1.4.Convertendo números decimais para binário
Há  vários métodos para se converter números decimais para binário; apenas um
será analisado aqui. Naturalmente a conversão com uma calculadora científica
é muito mais fácil, mas nem sempre podemos contar com isso, logo o mais
conveniente é, ao menos, sabermos uma fórmula para fazer-la.
O método resume-se na aplicação de divisões sucessivas por 2, mantendo o
resto como o dígito binário e o resultado como o próximo número a ser
dividido.
Tomemos como exemplo o número decimal 43.
43/2=21 e o resto ‚ 1; 
21/2=10 e o resto ‚ 1;
10/2= 5 e o resto ‚ 0;
 5/2= 2 e o resto ‚ 1;
 2/2= 1 e o resto ‚ 0;
 1/2= 0 e o resto ‚ 1.
Para construir o equivalente binário de 43, vamos pegar os restos obtidos de
baixo para cima, assim temos 101011.

 

2.2.1.5.Sistema hexadecimal
Na base hexadecimal temos 16 dígitos, que vão de 0 a 9 e da letra A até a F,
estas letras representam os números de 10 a 15. Portanto contamos:
0,1,2,3,4,5,6,7,8,9,A,B,C,D,E, e F.
A convers�o entre números binários e hexadecimais é fácil. A primeira coisa
a fazer é dividir o número binário em grupos de 4 bits, começando da direita
para a esquerda. Se no grupo mais à direita sobrarem dígitos, completamos
com zeros.
Tomando como exemplo o número binário 101011, vamos dividi-lo em grupos de 4
bits:
10;1011
Preenchendo o último grupo com zeros (o um mais à esquerda):
0010;1011
A seguir, tomamos cada grupo como um número independente e consideramos o
seu valor decimal:
0010=2;1011=11
Entretanto, observa-se que não podemos representar este número como 211,
isto seria um erro, uma vez que os números em hexa maiores que 9 e menores
que 16 são representados pelas letras A,B,...,F. Logo, obtemos como
resultado:
2Bh, onde o "h" representa a base hexadecimal.
Para a conversão de um número hexadecimal em binário é apenas necessário
inverter os passos: tomamos o primeiro dígito hexadecimal e o convertemos
para binário, a seguir o segundo, e assim por diante.
                                   
			--------------- // ---------------
2.2.2.Métodos de representação de dados num computador.
	2.2.2.1.Código ASCII
	2.2.2.2.Método BCD
	2.2.2.3.Representação de ponto flutuante
                            
2.2.2.1.C�digo ASCII
ASCII significa American Standard Code for Information Interchange. Este
código contém as letras do alfabeto, dígitos decimais de 0 a 9 e alguns
símbolos adicionais como um número binário de 7 bits, tendo o oitavo bit em
0, ou seja, desligado.
Deste modo, cada letra, d�gito ou caracter especial ocupa 1 byte na memória
do computador.
Podemos observar que este método de representação de dados é muito
ineficiente no aspecto numérico, uma vez que no formato binário 1 byte não é
suficiente para representar números de 0 a 255, com o ASCII podemos
representar apenas um dígito.
Devido a esta ineficiência, o código ASCII é usado, principalmente, para a
representação de textos.
                                
2.2.2.2.Método BCD ( Binary Coded Decimal)
Neste método grupos de 4 bits são usados para representar cada dígito
decimal de 0 a 9. Com este método podemos representar 2 dígitos por byte de
informação.
Vemos que este método vem a ser muito mais prático para representação
numérica do que o código ASCII. Embora ainda menos prático do que o binário,
com o método BCD podemos representar dígitos de 0 a 99. Com o binário, vemos
que o alcance é maior, de 0 a 255.
Este formato (BCD) é principalmente usado na representação de números
grandes, aplicações comerciais, devido às suas facilidades de operação.

                                 
2.2.2.3.Representação de ponto flutuante
Esta representação é baseada em notação científica, isto é, representar um
número em 2 partes: sua base e seu expoente.
Por exemplo o número decimal 1234000, é representado como 1.234*10^6,
observamos que o expoente ir  indicar o número de casas que o ponto decimal
deve ser movido para a direita, a fim de obtermos o número original.
O expoente negativo, por outro lado, indica o número de casas que o ponto
decimal deve se locomover para a esquerda.  
			--------------- // ---------------
2.3.PROGRAMA DEBUG
                   
Conteúdo:
2.3.1.Processo de criação de programas
2.3.2.Registradores da CPU
2.3.3.Programa debug
2.3.4.Estrutura Assembly
2.3.5.Criando um programa assembly simples
2.3.6.Armazenando e carregando os programas
                                   
2.3.1.Processo de criação de programas.
Para a criação de programas são necessários os seguintes passos:
   * Desenvolvimento do algoritmo, estágio em que o problema a ser
     solucionado é estabelecido e a melhor solução é proposta, criação de
     diagramas esquemáticos relativos à melhor solução proposta.
   * Codificação do algoritmo, o que consiste em escrever o programa
     em alguma linguagem de programação; linguagem assembly neste caso
     específico, tomando como base a solução proposta no passo anterior.
   * A transformação para a linguagem de máquina, ou seja, a criação
     do programa objeto, escrito como uma sequência de zeros e uns que podem
     ser interpretados pelo processador.
   * O último estágio é a eliminação de erros detectados no programa
     na fase de teste. A correção normalmente requer a repetição de todos os
     passos, com observação atenta.

              
2.3.2.Registradores da CPU.
Para o propósito didático, vamos focar registradores de 16 bits. A CPU
possui 4 registradores internos, cada um de 16 bits. São eles AX, BX, CX e
DX. São registradores de uso geral e também podem ser usados como
registradores de 8 bits. Para tanto devemos referenciá-los como, por
exemplo, AH e AL, que são, respectivamente, o byte high e o low do
registrador AX. Esta nomenclatura também se aplica para os registradores BX,
CX e DX.
Os registradores, segundo seus respectivos nomes:
 AX Registrador Acumulador
 BX Registrador Base
 CX Registrador Contador
 DX Registrador de Dados
 DS Registrador de Segmento de Dados
 ES Registrador de Segmento Extra
 SS Registrador de Segmento de Pilha
 CS Registrador de Segmento de Código
 BP Registrador Apontador da Base
 SI Registrador de Índice Fonte
 DI Registrador de Índice Destino
 SP Registrador Apontador de Pilha
 IP Registrador Apontador da Próxima Instrução
 F  Registrador de Flag

 

                         
2.3.3.Programa Debug.
Para a criação de um programa em assembler existem 2 opções: usar o TASM -
Turbo Assembler da Borland, ou o DEBUGGER. Nesta primeira seção vamos usar o
debug, uma vez que podemos encontrá-lo em qualquer PC com o MS-DOS.
Debug pode apenas criar arquivos com a extensão .COM, e por causa das
características deste tipo de programa, eles não podem exceder os 64 Kb, e
também devem iniciar no endereço de mem�ria 0100H dentro do segmento
específico. É importante observar isso, pois deste modo os programas .COM
não são relocáveis.
Os principais comandos do programa debug são:
A Montar instruções simbólicas em código de máquina
D Mostrar o conteúdo de uma área da memória
E Entrar dados na memória, iniciando num endereço específico
G Rodar um programa executável na memória
N Dar nome a um programa
P Proceder, ou executar um conjunto de instruções relacionadas
Q Sair do programa debug
R Mostrar o conteúdo de um ou mais registradores
T Executar passo a passo as instru‡�es
U Desmontar o código de máquina em instruções simbólicas
W Gravar um programa em disco
É possível visualizar os valores dos registradores internos da CPU usando o
programa Debug. Debug é um programa que faz parte do pacote do DOS, e pode
ser encontrado normalmente no diretório C:\DOS. Para iniciá-lo, basta
digitar Debug na linha de comando:
C:/>Debug [Enter]
-
Você notará então a presença de um hífen no canto inferior esquerdo da tela.
Não se espante, este é o prompt do programa. Para visualizar o conteúdo dos
registradores, experimente:
-r[Enter]
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D62  ES=0D62  SS=0D62  CS=0D62  IP=0100   NV UP EI PL NZ NA PO NC
0D62:0100 2E            CS:
0D62:0101 803ED3DF00    CMP     BYTE PTR [DFD3],00                 CS:DFD3=03
É mostrado o conteúdo de todos os registradores internos da CPU; um modo
alternativo para visualizar um único registrador ‚ usar o camando "r"
seguido do parâmetro que faz referência ao nome do registrador:
-rbx
BX 0000
:
Esta instrução mostrará o conteúdo do registrador BX e mudará o indicador do
Debug de "-" para ":"
Quando o prompt assim se tornar, significa que é possível, embora não
obrigatória, a mudança do valor contido no registrador, bastando digitar o
novo valor e pressionar [Enter]. Se você simplesmente pressionar [Enter] o
valor antigo se mantém.

 

2.3.4.Estrutura Assembly.
Nas linhas do código em Linguagem Assembly há duas partes: a primeira ‚ o
nome da instrução a ser executada; a segunda, os parâmetros do comando. Por
exemplo:
add ah bh
Aqui "add" ‚ o comando a ser executado, neste caso uma adição, e "ah" bem
como "bh" são os parâmetros.
Por exemplo:
mov al, 25
No exemplo acima, estamos usando a instrução mov, que significa mover o
valor 25 para o registrador al.
O nome das instruções nesta linguagem ‚ constituído de 2, 3 ou 4 letras.
Estas instruções são chamadas mnemônicos ou códigos de operação,
representando a função que o processador executar .
Às vezes instru‡�es aparecem assim:
add al,[170]
Os colchetes no segundo parâmetro indica-nos que vamos trabalhar com o
conteúdo da célula de memória de número 170, ou seja, com o valor contido no
endereço 170 da memória e não com o valor 170, isto é conhecido como
"endereçamento direto".

 

                                   
2.3.5.Criando um programa simples em assembly.
Não nos responsabilizaremos pela má  execução ou possíveis danos causados por
quaisquer exemplos que de agora em diante aparecerão, uma vez que os mesmos,
apesar de testados, são de caráter didático. Vamos, então, criar um programa
para ilustrar o que vimos até agora. Adicionaremos dois valores:
O primeiro passo ‚ iniciar o Debug, o que j  vimos como fazer anteriormente.
Para montar um programa no Debug, ‚ usado o comando "a" (assemble); 
quando usamos este comando, podemos especificar um endereço inicial para 
o nosso programa como o parâmetro, mas é opcional. No caso de omissão, 
o endereço inicial é o especificado pelos registradores CS:IP, geralmente 
0100h, o local em que programas com extensão .COM devem iniciar. E será  
este o local que usaremos, uma vez que o Debug só pode criar este tipo de programa.
Embora neste momento n�o seja necess rio darmos um parƒmetro ao comando "a",
isso ‚ recomend vel para evitar problemas, logo:
a 100[enter]
mov ax,0002[enter]
mov bx,0004[enter]
add ax,bx[enter]
nop[enter][enter]
O que o programa faz? Move o valor 0002 para o registrador ax, move o valor
0004 para o registrador bx, adiciona o conteúdo dos registradores ax e bx,
guardando o resultado em ax e finalmente a instrução nop (nenhuma operação)
finaliza o programa.
No programa debug, a tela se parecerá com:
C:\>debug
-a 100
0D62:0100 mov ax,0002
0D62:0103 mov bx,0004
0D62:0106 add ax,bx
0D62:0108 nop
0D62:0109
Entramos com o comando "t" para executar passo a passo as instruções:
-t
AX=0002  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D62  ES=0D62  SS=0D62  CS=0D62  IP=0103   NV UP EI PL NZ NA PO NC
0D62:0103 BB0400        MOV     BX,0004
Vemos o valor 0002 no registrador AX. Teclamos "t" para executar a segunda
instrução:
-t
AX=0002  BX=0004  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D62  ES=0D62  SS=0D62  CS=0D62  IP=0106   NV UP EI PL NZ NA PO NC
0D62:0106 01D8          ADD     AX,BX
Teclando "t" novamente para ver o resultado da instrução add:
-t
AX=0006  BX=0004  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D62  ES=0D62  SS=0D62  CS=0D62  IP=0108   NV UP EI PL NZ NA PE NC
0D62:0108 90            NOP
A possibilidade dos registradores conterem valores diferentes existe, mas AX
e BX devem conter os mesmos valores acima descritos.
Para sair do Debug usamos o comando "q" (quit).
                                  
2.3.6.Armazenando e carregando os programas.
Não seria prático ter que digitar o programa cada vez que iniciássemos o
Debug. Ao invés disso, podemos armazená-lo no disco. Só que o mais
interessante nisso é que um simples comando de salvar cria um arquivo com a
extensão .COM, ou seja, executável - sem precisarmos efetuar os processos de
montagem e ligação, como veremos posteriormente com o TASM.
Eis os passos para salvar um programa que já esteja na memória:
      * Obter o tamanho do programa subtraindo o endereço final do
        endereço inicial, naturalmente que no sistema hexadecimal.
      * Dar um nome ao programa.
      * Colocar o tamanho do programa no registrador CX.
      * Mandar o debug gravar o programa em disco.
Usando como exemplo o seguinte programa, vamos clarear a idéia de como
realizar os passos acima descritos:
0C1B:0100 mov ax,0002
0C1B:0103 mov bx,0004
0C1B:0106 add ax,bx
0C1B:0108 int 20
0C1B:010A
Para obter o tamanho de um programa, o comando "h" é usado, já que ele nos
mostra a adição e subtração de dois números em hexadecimal. Para obter o
tamanho do programa em questão, damos como parâmetro o valor do endereço
final do nosso programa (10A), e o endereço inicial (100). O primeiro
resultado mostra-nos a soma dos endereços, o segundo, a subtração.
-h 10a 100
020a 000a
O comando "n" permite-nos nomear o programa.
-n test.com
O comando "rcx" permite-nos mudar o conteúdo do registrador CX para o valor
obtido como tamanho do arquivo com o comando "h", neste caso 000a.
-rcx
CX 0000
:000a
Finalmente, o comando "w" grava nosso programa no disco, indicando quantos
bytes gravou.
-w
Writing 000A bytes
Para já salvar um arquivo quando carregá-lo, 2 passos são necessários:
      Dar o nome do arquivo a ser carregado.
      Carregá-lo usando o comando "l" (load).
Para obter o resultado correto destes passos, é necessário que o programa
acima já esteja criado.
Dentro do Debug, escrevemos o seguinte:
-n test.com
-l
-u 100 109
0C3D:0100 B80200 MOV AX,0002
0C3D:0103 BB0400 MOV BX,0004
0C3D:0106 01D8 ADD AX,BX
0C3D:0108 CD20 INT 20
O último comando "u" ‚ usado para verificar que o programa foi carregado na
memória. O que ele faz ‚ desmontar o código e mostrá-lo em assembly. Os 
parâmetros indicam ao Debug os endereços inicial e final a serem desmontados.
O Debug sempre carrega os programas na mem�ria no endereço 100h, conforme já 
comentamos.
 
*****************************************************************************