Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas

Tutorial Linguagem Assembly, Notas de estudo de Informática

Tutorial em Portugues da linguagem de programaçao assembly

Tipologia: Notas de estudo

Antes de 2010
Em oferta
30 Pontos
Discount

Oferta por tempo limitado


Compartilhado em 23/10/2009

arnaldo-araujo-11
arnaldo-araujo-11 🇧🇷

4.3

(40)

71 documentos

Pré-visualização parcial do texto

Baixe Tutorial Linguagem Assembly e outras Notas de estudo em PDF para Informática, somente na Docsity! **************************************************************************** T U T O R I A L D E L I N G U A G E M A S S E M B L Y --------------- --- ----------------- --------------- Conteúdo: 1.Introdução 2.Conceitos Básicos 3.Programação Assembly 4.Instruções Assembly 5.Interrupções e gerência de arquivos 6.Macros e procedimentos 7.Exemplos de programas 8.Bibliografia ***************************************************************************** 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.4.Nós precisamos da sua opinião --------------- // --------------- 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 a 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 aquelas 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 assembly é 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 assembly. 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. 1.4.Nós precisamos da sua opinião: Nosso intuito é oferecer um modo simples para que você consiga aprender Assembly por si mesmo. Por tanto, qualquer comentário ou sugestão ser bem-vinda. ***************************************************************************** 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 2.1.1.Processador Central 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 ... 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. O caracter ^ é usado em computação como símbolo para potência e * para a multiplicação. 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 Vai 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 fazê-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 a 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 BCD significa 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 as 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 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 seqüê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. 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] <- Ponteiro 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. Segundo passo Salvar o arquivo com o seguinte nome: exam1.asm Não esquecer de salvá-lo no formato ASCII. Terceiro passo Usar o programa TASM para construir o programa objeto. Exemplo: C:\>tasm exam1.asm Turbo Assembler Version 2.0 Copyright (c) 1988, 1990 Borland International Assembling file: exam1.asm Error messages: None Warning messages: None Passes: 1 Remaining memory: 471k O TASM só pode criar programas no formato .OBJ, que ainda não pode ser executado... Quarto passo Usar o programa TLINK para criar o programa executável. Exemplo: C:\>tlink exam1.obj Turbo Link Version 3.0 Copyright (c) 1987, 1990 Borland International C:\> Onde exam1.obj é o nome do programa intermediário, .OBJ. O comando acima gera diretamente o arquivo com o nome do programa intermediário e a extensão .EXE. É opcional a colocação da extensão .obj no comando. Quinto passo Executar o programa executável criado. C:\>exam1[enter] Lembre-se, este programa assembly muda o tamanho do cursor no DOS. --------------- // --------------- 3.2.Processo Assembly. 3.2.1.Segmentos 3.2.2.Tabela de equivalência 3.2.1.SEGMENTOS A arquitetura dos processadores x86 força-nos a usar segmentos de memória para gerenciar a informação, o tamanho destes segmentos é de 64Kb. A razão de ser destes segmentos é que, considerando que o tamanho máximo de um número que o processador pode gerenciar é dado por uma palavra de 16 bits ou registrador, assim não seria possível ter acesso a mais do que 65536 locais da memória usando apenas um destes registradores. Mas agora, se a memória do PC é dividida em grupos de segmentos, cada um com 65536 locais, e podemos usar um endereço ou registrador exclusivo para encontrar cada segmento, e ainda fazemos cada endereço de um específico slot com dois registradores, nos é possível ter acesso à quantidade de 4294967296 bytes de memória, que é, atualmente, a maior memória que podemos instalar num PC. Desta forma, para que o montador seja capaz de gerenciar os dados, se faz necessário que cada informação ou instrução se encontre na área correspondente ao seu segmento. O endereço do segmento é fornecido ao montador pelos registradores DS, ES, SS e CS. Lembrando um programa no Debug, observe: 1CB0:0102 MOV AX,BX O primeiro número 1CB0, corresponde ao segmento de memória que está sendo usado, o segundo é uma referência ao endereço dentro do segmento, é um deslocamento dentro do segmento offset. O modo usado para indicar ao montador com quais segmentos vamos trabalhar é fazendo uso das diretivas .CODE, .DATA e .STACK. O montador ajusta o tamanho dos segmentos tomando como base o número de bytes que cada instrução assembly precisa, já que seria um desperdício de memória usar segmentos inteiros. Por exemplo, se um programa precisa de apenas 10Kb para armazenar dados, o segmento de dados seria apenas de 10Kb e não de 64Kb, como poderia acontecer se feito manualmente. 3.2.2.TABELAS DE EQUIVALÊNCIA Cada uma das partes numa linha de código assembly é conhecida como token, por exemplo: MOV AX,Var Aqui temos três tokens, a instrução MOV, o operador AX e o operador VAR. O que o montador faz para gerar o código OBJ é ler cada um dos tokens e procurar a equivalência em código de máquina em tabelas correspondentes, seja de palavras reservadas, tabela de códigos de operação, tabela de símbolos, tabela de literais, onde o significado dos mnemônicos e os endereços dos símbolos que usamos serão encontrados. A maioria dos montadores são de duas passagens. Em síntese na primeira passagem temos a definição dos símbolos, ou seja, são associados endereços a todas as instruções do programa. Seguindo este processo, o assembler lê MOV e procura-o na tabela de códigos de operação para encontrar seu equivalente na linguagem de máquina. Da mesma forma ele lê AX e encontra-o na tabela correspondente como sendo um registrador. O processo para Var é um pouco diferenciado, o montador verifica que ela não é uma palavra reservada, então procura na tabela de símbolos, lá encontrando-a ele designa o endereço correspondente, mas se não encontrou ele a insere na tabela para que ela possa receber um endereço na segunda passagem. Ainda na primeira passagem é executado parte do processamento das diretivas, é importante notar que as diretivas não criam código objeto. Na passagem dois são montadas as instruções, traduzindo os códigos de operação e procurando os endereços, e é gerado o código objeto. Há símbolos que o montador não consegue encontrar, uma vez que podem ser declarações externas. Neste caso o linker entra em ação para criar a estrutura necessária a fim de ligar as diversas possíveis partes de código, dizendo ao loader que o segmento e o token em questão são definidos quando o programa é carregado e antes de ser executado. --------------- // --------------- 3.3.Mais programas. Outro exemplo Primeiro passo Use qualquer editor e crie o seguinte: ;exemplo2 .model small .stack .code mov ah,2h ;move o valor 2h para o registrador ah mov dl,2ah ;move o valor 2ah para o registrador dl ;(é o valor ASCII do caractere *) int 21h ;interrupção 21h mov ah,4ch ;função 4ch, sai para o sistema operacional int 21h ;interrupção 21h end ;finaliza o programa Segundo passo Salvar o arquivo com o nome: exam2.asm Não esquecer de salvar em formato ASCII. Terceiro passo Usar o programa TASM para construir o programa objeto. C:\>tasm exam2.asm Turbo Assembler Version 2.0 Copyright (c) 1988, 1990 Borland International Assembling file: exam2.asm Error messages: None Warning messages: None Passes: 1 Remaining memory: 471k Quarto passo Usar o programa TLINK para criar o programa executável. C:\>tlink exam2.obj Turbo Link Version 3.0 Copyright (c) 1987, 1990 Borland International C:\> Quinto passo Executar o programa: C:\>exam2[enter] * C:\> Este programa imprime o caracter * na tela. --------------- // --------------- 3.4.Tipos de instruções. 3.4.1.Movimento de dados 3.4.2.Operações lógicas e aritméticas 3.4.3.Saltos, laços e procedimentos MOV Destino, Fonte Destino é o lugar para onde o dado ser movido e Fonte é o lugar onde o dado está. Os diferentes movimentos de dados permitidos para esta instrução são: *Destino: memória. Fonte: acumulador *Destino: acumulador. Fonte: memória *Destino: registrador de segmento. Fonte: memória/registrador *Destino: memória/registrador. Fonte: registrador de segmento *Destino: registrador. Fonte: registrador *Destino: registrador. Fonte: memória *Destino: memória. Fonte: registrador *Destino: registrador. Fonte: dado imediato *Destino: memória. Fonte: dado imediato Exemplo: MOV AX,0006h MOV BX,AX MOV AX,4C00h INT 21h Este pequeno programa move o valor 0006h para o registrador AX, então ele move o conteúdo de AX (0006h) para o registrador BX, e finalmente move o valor 4C00h para o registrador AX para terminar a execução com a opção 4C da interrupção 21h. INSTRUÇÕES MOVS (MOVSB) (MOVSW) Propósito: Mover byte ou cadeias de palavra da fonte, endereçada por SI, para o destino endereçado por DI. Sintaxe: MOVS Este comando não necessita de parâmetros uma vez que toma como endereço fonte o conteúdo do registrador SI e como destino o conteúdo de DI. A seguinte seqüência de instruções ilustra isso: MOV SI, OFFSET VAR1 MOV DI, OFFSET VAR2 MOVS Primeiro inicializamos os valores de SI e DI com os endereços das variáveis VAR1 e VAR2 respectivamente, então após a execução de MOVS o conteúdo de VAR1 é copiado para VAR2. As instruções MOVSB e MOVSW são usadas do mesmo modo que MOVS, a primeira move um byte e a segunda move uma palavra. 4.1.2.Instruções de carga. São instruções específicas para registradores, usadas para carregar bytes ou cadeias de bytes num registrador. LODS (LODSB) (LODSW) LAHF LDS LEA LES INSTRUÇÕES LODS (LODSB) (LODSW) Propósito: Carregar cadeias de um byte ou uma palavra para o acumulador. Sintaxe: LODS Esta instrução toma a cadeia encontrada no endereço especificado por SI, a carrega para o registrador AL (ou AX) e adiciona ou subtrai, dependendo do estado de DF, para SI se é uma transferência de bytes ou de palavras. MOV SI, OFFSET VAR1 LODS Na primeira linha vemos a carga do endereço de VAR1 em SI e na segunda é tomado o conteúdo daquele local para o registrador AL. Os comandos LODSB e LODSW são usados do mesmo modo, o primeiro carrega um byte e o segundo uma palavra (usa todo o registrador AX). INSTRUÇÃO LAHF Propósito: Transferir o conteúdo dos flags para o registrador AH. Sintaxe: LAHF Esta instrução é útil para verificar o estado dos flags durante a execução do nosso programa. Os flags são deixados na seguinte ordem dentro do registrador: SF ZF ?? AF ?? PF ?? CF O "??" significa que haverá um valor indefinido naqueles bits. INSTRUÇÃO LDS Propósito: Carregar o registrador de segmento de dados. Sintaxe: LDS destino, fonte O operador fonte deve ser uma double word na memória. A palavra associada com o maior endereço é transferida para DS, em outras palavras isto é tomado como o endereço de segmento. A palavra associada com o menor endereço é o endereço de deslocamento e é depositada no registrador indicado como destino. INSTRUÇÃO LEA Propósito: Carregar o endereço do operador fonte. Sintaxe: LEA destino, fonte O operador fonte deve estar localizado na memória, e seu deslocamento é colocado no registrador de índice ou ponteiro especificado no destino. Para ilustrar uma das facilidades que temos com este comando, vejamos: MOV SI,OFFSET VAR1 É equivalente a: LEA SI,VAR1 É muito provável que para o programador é muito mais fácil criar programas grandes usando este último formato. INSTRUÇÃO LES Propósito: Carregar o registrador de segmento extra Sintaxe: LES destino, fonte O operador fonte deve ser uma palavra dupla na memória. O conteúdo da palavra com endereço maior é interpretado como o endereço do segmento e é Fonte Destino | Destino ----------------------------- 1 1 | 1 1 0 | 0 0 1 | 0 0 0 | 0 O resultado desta operação é armazenado no operador de destino. INSTRUÇÃO NEG Propósito: Gera o complemento de 2. Sintaxe: NEG destino Esta instrução gera o complemento de 2 do operador destino e o armazena no mesmo operador. Por exemplo, if AX armazena o valor 1234H, então: NEG AX Isto fará com o que o valor EDCCH fique armazenado no registrador AX. INSTRUÇÃO NOT Propósito: Faz a negação do operador de destino bit a bit. Sintaxe: NOT destino O resultado é armazenado no mesmo operador de destino. INSTRUÇÃO OR Propósito: realiza um OU lógico. Sintaxe: OR destino, fonte A instrução OR, faz uma disjunção lógica bit a bit dos dois operadores: Fonte Destino | Destino ----------------------------------- 1 1 | 1 1 0 | 1 0 1 | 1 0 0 | 0 INSTRUÇÃO TEST Propósito: Compara logicamente os operadores. Sintaxe: TEST destino, fonte Realiza uma conjunção, bit a bit, dos operadores, mas difere da instrução AND, uma vez que não coloca o resultado no operador de destino. Tem efeito sobre o registrador de flag. INSTRUÇÃO XOR Propósito: realiza um OU exclusivo. Sintaxe: XOR destino, fonte Esta instrução realiza uma disjunção exclusiva de dois operadores bit a bit. Fonte Destino | Destino ----------------------------------- 1 1 | 0 0 0 | 1 0 1 | 1 0 0 | 0 4.2.2.Instruções aritméticas. São usadas para realizar operações aritméticas nos operadores. ADC ADD DIV IDIV MUL IMUL SBB SUB INSTRUÇÃO ADC Propósito: Efetuar a soma entre dois operandos com carry. Sintaxe: ADC destino, fonte Esta instrução efetua a soma entre dois operandos, mais o valor do flag CF, existente antes da operação. Apenas o operando destino e os flags são afetados. O resultado é armazenado no operador de destino. INSTRUÇÃO ADD Propósito: Adição de dois operadores. Sintaxe: ADD destino, fonte Esta instrução adiciona dois operadores e armazena o resultado no operador destino. INSTRUÇÃO DIV Propósito: Divisão sem sinal. Sintaxe: DIV fonte O divisor pode ser um byte ou uma palavra e é o operador que é dado na instrução. Se o divisor é de 8 bits, o registrador AX de 16 bits é tomado como dividendo e se o divisor é de 16 bits, o par de registradores DX:AX será tomado como dividendo, tomando a palavra alta de DX e a baixa de AX. Se o divisor for um byte, então o quociente será armazenado no registrador AL e o resto em AH. Se for uma palavra, então o quociente é armazenado em AX e o resto em DX. INSTRUÇÃO IDIV Propósito: Divisão com sinal. Sintaxe: IDIV fonte Consiste basicamente como a instrução DIV, diferencia-se apenas por realizar a operação com sinal. Para os resultados são usados os mesmos registradores da instrução DIV. INSTRUÇÃO MUL Propósito: Multiplicação com sinal. Sintaxe: MUL fonte Esta instrução realiza uma multiplicação não sinalizada entre o conteúdo do acumulador AL ou AX pelo operando-fonte, devolvendo o resultado no acumulador AX caso a operação tenha envolvido AL com um operando de 8 bits, ou em DX e AX caso a operação tenha envolvido AX e um operando de 16 bits. INSTRUÇÃO IMUL Propósito: Multiplicação de dois números inteiros com sinal. Sintaxe: IMUL fonte Esta instrução faz o mesmo que a anterior, difere apenas pela inclusão do sinal. Os resultados são mantidos nos mesmos registradores usados pela instrução MUL. INSTRUÇÃO SBB Propósito: Subtração com carry. Sintaxe: SBB destino, fonte Esta instrução subtrai os operadores e subtrai um do resultado se CF está ativado. O operador fonte é sempre subtraído do destino. Uso: Ler informação de um dispositivo ou de um arquivo. Registradores de chamada: AH = 3FH BX = Número do handle CX = Número de bytes a ler DS:DX = Área para receber o dado Registradores de retorno: CF = 0 se não há erro e AX = número de bytes lidos. CF = 1 se há erro e AX conterá o código de erro. FUNÇÃO 0FH Uso: Abrir um arquivo FCB. Registradores de chamada: AH = 0FH DS:DX = Ponteiro para um FCB Registradores de retorno: AL = 00H se não há problemas, de outra forma retorna 0FFH FUNÇÃO 14H Uso: Leitura seqüencial num arquivo FCB. Registradores de chamada: AH = 14H DS:DX = Ponteiro para um FCB já aberto. Registradores de retorno: AL = 0 se não há erros, de outra forma o código correspondente de erro retornará: 1 erro no fim do arquivo, 2 erro na estrutura FCB e 3 erro de leitura parcial. O que esta função faz é ler o próximo bloco de informações do endereço dado por DS:DX, e atualizar este registro. FUNÇÃO 15H Uso: Escrita seqüencial e arquivo FCB. Registradores de chamada: AH = 15H DS:DX = Ponteiro para um FCB já aberto. Registradores de retorno: AL = 00H se não há erros, de outra forma conterá o código de erro: 1 disco cheio ou arquivo somente de leitura, 2 erro na formação ou na especificação do FCB. A função 15H atualiza o FCB após a escrita do registro para o presente bloco. FUNÇÃO 16H Uso: Criar um arquivo FCB. Registradores de chamada: AH = 16H DS:DX = Ponteiro para um FCB já aberto. Registradores de retorno: AL = 00H se não há erros, de outra forma conterá o valor 0FFH. É baseada na informação advinda de um FCB para criar um arquivo num disco. FUNÇÃO 21H Uso: Ler de modo randômico um arquivo FCB. Registradores de chamada: AH = 21H DS:DX = Ponteiro para FCB aberto. Registradores de retorno: A = 00H se não há erro, de outra forma AH conterá o código de erro: 1 se é o fim do arquivo, 2 se há um erro de especificação no FCB e 3 se um registro foi lido parcialmente ou o ponteiro de arquivo está no fim do mesmo. Esta função lê o registro especificado pelos campos do bloco atual e registro de um FCB aberto e coloca a informação na DTA, Área de Transferência do Disco. FUNÇÃO 22H Uso: Escrita randômica num arquivo FCB. Registradores de chamada: AH = 22H DS:DX = Ponteiro para um FCB aberto. Registradores de retorno: AL = 00H se não há erro, de outra forma conterá o código de erro: 1 se o disco está cheio ou o arquivo é apenas de leitura e 2 se há um erro na especificação FCB. Escreve o registro especificado pelos campos do bloco atual e registro de um FCB aberto. Esta informação é do conteúdo da DTA. FUNÇÃO 3CH Uso: Criar um arquivo se não existe ou deixá-lo com comprimento 0 se existe. Registradores de chamada: AH = 3CH CH = Atributo do arquivo DS:DX = Nome do arquivo, no formato ASCII. Registradores de retorno: CF = 0 e AX informa o número do handle se não há erro. Se caso houver erro, CF ser 1 e AX conterá o código de erro: 3 caminho não encontrado, 4 não há handles disponíveis e 5 acesso negado. Esta função substitui a função 16H. O nome do arquivo é especificado numa cadeia ASCII de bytes terminados pelo caracter 0. O arquivo criado conterá os atributos definidos no registrador CX, do seguinte modo: Valor Atributos 00H Normal 02H Hidden 04H System 06H Hidden e System O arquivo é criado com permissão de leitura e escrita. Não é possível a criação de diretórios através desta função. FUNÇÃO 3DH Uso: Abre um arquivo e retorna um handle. Registradores de chamada: AH = 3DH AL = modo de acesso DS:DX = Nome do arquivo, no formato ASCII. Registradores de retorno: CF = 0 e AX = número do handle se não há erros, de outra forma CF = 1 e AX = código de erro: 01H se a função não é v lida, 02H se o arquivo não foi Nenhum. A principal diferença entre esta função e a anterior é permitir mudança nos atributos, bem como mudar a posição do cursor. Função 0Eh Uso: Exibir um caracter na tela do computador atualizando a posição do cursor. Registradores de chamada: AH = 0EH AL = Caracter a exibir BH = Página de vídeo onde o caracter será exibido BL = Cor a usar (apenas em modo gráfico) Registradores de retorno: Nenhum --------------- // --------------- 5.1.4.3.Interrupção 16H Veremos duas funções da interrupção 16H. A exemplo das demais interrupções, usa-se o registrador AH para chamá-las. Funções da interrupção 16h Função 00H, lê um caracter do teclado. Função 01H, lê o estado atual do teclado. Função 00H Uso: Ler um caracter do teclado. Registradores de chamada: AH = 00H Registradores de retorno: AH = Código da tecla pressionada AL = Valor ASCII do caracter Quando se usa esta interrupção, os programas executam até que uma tecla seja pressionada. Se é um valor ASCII, é armazenado no registrador AH. Caso contrário, o código é armazenado no registrador AL e AH=0. Este valor de AL pode ser utilizado quando queremos detectar teclas que não estão diretamente representadas pelo seu valor ASCII, tais como [ALT][CONTROL]. Função 01h Uso: Ler o estado do teclado Registradores de chamada: AH = 01H Registradores de retorno: Se o registrador de flag é zero, significa que há informação no buffer de teclado na memória. Caso contrário, o buffer está vazio. Portanto o valor do registrador AH será o valor da tecla armazenada no buffer. --------------- // --------------- 5.1.4.4.Interrupção 17H Propósito: Manusear a entrada e saída da impressora. Sintaxe: Int 17H Esta interrupção é usada para enviar caracteres, setar ou ler o estado de uma impressora. Funções da interrupção 17h Função 00H, imprime um valor ASCII Função 01H, seta a impressora Função 02H, lê estado da impressora Função 00H Uso: Imprimir um caracter numa impressora. Registradores de chamada: AH = 00H AL = Caracter a imprimir DX = Porta de conexão Registradores de retorno: AH = Estado da impressora Os valores da porta a colocar no registrador DX são: LPT1 = 0, LPT2 = 1, LPT3 = 2 ... O estado da impressora é codificado bit a bit como segue: BIT 1/0 SIGNIFICADO ---------------------------------------- 0 1 Estado de time-out 1 - 2 - 3 1 Erro de entrada e saída 4 1 Impressora selecionada 5 1 Fim de papel 6 1 Reconhecimento de comunicação 7 1 A impressora está pronta para o uso Os bits 1 e 2 bits não são relevantes A maioria dos BIOS suportam 3 portas paralelas, havendo alguns que suportam 4. Função 01h Uso: Setar uma porta paralela. Registradores de chamada: AH = 01H DX = Porta Registradores de retorno: AH = Status da impressora A porta definida no registrador DX pode ser: LPT1=0, LPT2=1, assim por diante. O estado da impressora é codificado bit a bit como segue: BIT 1/0 SIGNIFICADO ---------------------------------------- 0 1 Estado de time-out 1 - 2 - 3 1 Erro de entrada e saída 4 1 Impressora selecionada 5 1 Fim de papel 6 1 Reconhecimento de comunicação 7 1 A impressora está pronta para o uso Os bits 1 e 2 bits não são relevantes Função 02h Uso: Obter o status da impressora. Registradores de chamada: AH = 01H DX = Porta Registradores de retorno AH = Status da impressora A porta definida no registrador DX pode ser: LPT1=0, LPT2=1, assim por diante. Para a criação de arquivos é usada a função 16H da interrupção 21h. O registrador DX deve apontar para uma estrutura de controle cujo os requisitos são de que pelo menos a unidade lógica, o nome e a extensão do arquivo sejam definidas. Caso ocorra problema, o valor FFH deve retornar em AL, de outra forma este registrador conterá o valor 0. --------------- // --------------- 5.2.2.4.ESCRITA SEQÜENCIAL Antes de conseguirmos realizar escrita para o disco, é necessário definir a área de transferência de dados usando, para tanto, a função 1AH da interrupção 21h. A função 1AH não retorna qualquer estado do disco nem da operação. Mas a função 15H, que usaremos para escrever para o disco, faz isso no registrador AL. Se este for igual a zero, então não há erro e os campos de registro corrente e de bloco são atualizados. --------------- // --------------- 5.2.2.5.LEITURA SEQÜENCIAL Antes de tudo, devemos definir a área de transferência de arquivo ou DTA. Para a leitura seqüencial usaremos a função 14H da interrupção 21h. O registro a ser lido é definido pelos campos registro e bloco corrente. O registrador AL retorna o estado da operação. Se AL contém o valor 1 ou 3, significa que foi atingido o fim do arquivo. Um valor 2, por sua vez, significa que o FCB está estruturado erroneamente. Caso não ocorra erro, AL conterá o valor 0 e os campos de registro e bloco corrente são atualizados. --------------- // --------------- 5.2.2.6.LEITURA E ESCRITA RANDÔMICA A função 21H e a função 22H da interrupção 21h são usadas na realização, respectivamente, da escrita e leitura randômica. O número de registro randômico e o bloco corrente são usados para calcular a posição relativa do registro a ser lido ou escrito. O registrador AL retorna a mesma informação do que para escrita e leitura seqüencial. A informação a ser lida ser retornada na área de transferência do disco, bem como a informação a ser escrita retorna na DTA. --------------- // --------------- 5.2.2.7.FECHAR UM ARQUIVO Para fechar um arquivo usamos a função 10H da interrupção 21h. Se após invocar esta função, o registrador AL conter o valor FFH, significa que o arquivo foi mudado de posição, o disco foi mudado ou h erro de acesso a disco. --------------- // --------------- 5.2.3.Canais de comunicação. 5.2.3.1.Trabalhando com handles 5.2.3.2.Funções para usar handles 5.2.3.1.TRABALHANDO COM HANDLES O uso de handles para gerenciar arquivos traz grandes facilidades na criação de arquivos e o programador pode concentrar-se em outros aspectos da programação sem preocupar-se com detalhes que podem ser manuseados pelo sistema operacional. A facilidade dos handles consiste em que para operarmos sobre um arquivo é apenas necessário definirmos o nome do mesmo e o número de handle a usar, todo o resto da informação é manuseada internamente pelo DOS. Quando usamos este método para trabalhar com arquivos, não há distinção entre acesso seqüencial ou randômico, o arquivo é simplesmente tomado como uma rede de bytes. --------------- // --------------- 5.2.3.2.FUNÇÕES PARA USAR HANDLES As funções usadas para o manuseio de arquivos através de handles são descritas na Página sobre: Interrupções, na seção dedicada á interrupção 21h. ***************************************************************************** CAPÍTULO 6: MACROS E PROCEDIMENTOS Conteúdo 6.1.Procedimentos 6.2.Macros 6.1.Procedimentos 6.1.1.Definição de procedimento 6.1.2.Sintaxe de um procedimento --------------- // --------------- 6.1.1.Definição de um procedimento Um procedimento é uma coleção de instruções para as quais é possível direcionar o curso de nosso programa, e uma vez que a execução destas instruções do procedimento tenha acabado, o controle retorna para linha que segue à que chamou o procedimento. Procedimentos nos ajudam a criar programas legíveis e fáceis de modificar. Quando se invoca um procedimento, o endereço da próxima instrução do programa é mantido na pilha, de onde é recuperado quando do retorno do procedimento. --------------- // --------------- 6.1.2.Sintaxe de um procedimento Há dois tipos de procedimentos, os intrasegments, que se localizam no mesmo segmento da instrução que o chama, e os inter segments, que podem se localizar em diferentes segmentos de memória. Quando os procedimentos intrasegments são usados, o valor de IP é armazenado na pilha e quando os procedimentos inter segments são usados o valor de CS:IP é armazenado. Lembre-se que o registrador CS indica qual o segmento de código. A diretiva que chama um procedimento é como segue: CALL NomedoProcedimento As partes que compõem um procedimento são as seguintes: Declaração do procedimento Código do procedimento Diretiva de retorno Término do procedimento Por exemplo, se quisermos uma rotina que soma dois bytes armazenados em AH e AL, e o resultado da soma em BX: Soma Proc Near ; Declaração do Procedimento Mov BX, 0 ; Conteúdo do Procedimento... Mov BL, AH Mov AH, 00 Add BX, AX Ret ; Diretiva de retorno Soma EndP ; Fim do Procedimento Na declaração, a primeira palavra, Soma, corresponde ao nome do procedimento. Proc declara-o e a palavra Near indica que o procedimento é do tipo intrasegment, ou seja, no mesmo segmento. A diretiva Ret carrega IP com o endereço armazenado na pilha para retornar ao programa que chamou. Finalmente, Soma EndP indica o fim do procedimento. Para declarar um procedimento inter segment, basta substituir a palavra Near para FAR. A chamada deste procedimento é feito de modo idêntico: Call Soma Macros oferecem uma grande flexibilidade na programação, comparadas aos procedimentos. --------------- // --------------- 6.2.Macros 6.2.1.Definição de uma Macro Por exemplo: C:\>debug -n one.com -l -u 100 109 0D80:0100 B80600 MOV AX,0006 0D80:0103 BB0400 MOV BX,0004 0D80:0106 01D8 ADD AX,BX 0D80:0108 CD20 INT 20 - Nota: -n one.com Dar nome ao programa a ser carregado -l Carregá-lo -u 100 109 Desmontar o código do endereço inicial ao final especificado Segundo passo Digite o comando g Por exemplo: -g Program terminated normally - Exemplos de programas no Debug Primeiro exemplo -a0100 297D:0100 MOV AX,0006 ;Põe o valor 0006 no registrador AX 297D:0103 MOV BX,0004 ;Põe o valor 0004 no registrador BX 297D:0106 ADD AX,BX ;Adiciona BX ao conteúdo de AX 297D:0108 INT 20 ;Finaliza o Programa A única coisa que este programa faz é salvar dois valores em dois registradores e adicionar o valor de um ao outro. Segundo exemplo - a100 0C1B:0100 jmp 125 ;Salta para o endereço 125h 0C1B:0102 [Enter] - e 102 'Hello, How are you ?' 0d 0a '$' - a125 0C1B:0125 MOV DX,0102 ;Copia a string para registrador DX 0C1B:0128 MOV CX,000F ;Quantas vezes a string será mostrada 0C1B:012B MOV AH,09 ;Copia o valor 09 para registrador AH 0C1B:012D INT 21 ;Mostra a string 0C1B:012F DEC CX ;Subtrai 1 de CX 0C1B:0130 JCXZ 0134 ;Se CX é igual a 0 salta para o endereço 0134 0C1B:0132 JMP 012D ;Salta ao endereço 012D 0C1B:0134 INT 20 ;Finaliza o programa Este programa mostra 15 vezes na tela a string de caracteres. Terceiro exemplo -a100 297D:0100 MOV AH,01 ;Função para mudar o cursor 297D:0102 MOV CX,0007 ;Formata o cursor 297D:0105 INT 10 ;Chama interrupção do BIOS 297D:0107 INT 20 ;Finaliza o programa Este programa muda o formato do cursor. Quarto exemplo -a100 297D:0100 MOV AH,01 ;Função 1 (lê caractere do teclado) 297D:0102 INT 21 ;Chama interrupção do DOS 297D:0104 CMP AL,0D ;Compara se o caractere lido é um ENTER 297D:0106 JNZ 0100 ;Se não é, lê um outro caractere 297D:0108 MOV AH,02 ;Função 2 (escreve um caractere na tela) 297D:010A MOV DL,AL ;Character to write on AL 297D:010C INT 21 ;Chama interrupção do DOS 297D:010E INT 20 ;Finaliza o programa Este programa usa a interrupção 21h do DOS. Usa duas funções da mesma: a primeira lê um caractere do teclado (função 1) e a segundo escreve um caractere na tela. O programa lê caracteres do teclado até encontrar um ENTER. Quinto exemplo -a100 297D:0100 MOV AH,02 ;Função 2 (escreve um caractere na tela) 297D:0102 MOV CX,0008 ;Põe o valor 0008 no registrador CX 297D:0105 MOV DL,00 ;Põe o valor 00 no registrador DL 297D:0107 RCL BL,1 ;Rotaciona o byte em BL um bit para a esquerda 297D:0109 ADC DL,30 ;Converte o registrador de flag para 1 297D:010C INT 21 ;Chama interrupção do DOS 297D:010E LOOP 0105 ;Salta se CX > 0 para o endereço 0105 297D:0110 INT 20 ;Finaliza o programa Este programa mostra na tela um número binário através de um ciclo condicional (LOOP) usando a rotação do byte. Sexto exemplo -a100 297D:0100 MOV AH,02 ;Função 2 (escreve um caractere na tela) 297D:0102 MOV DL,BL ;Põe o valor de BL em DL 297D:0104 ADD DL,30 ;Adiciona o valor 30 a DL 297D:0107 CMP DL,3A ;Compara o valor 3A com o conteúdo de DL ;sem afetá-lo, seu valor apenas modifica o ;estado do flag de carry 297D:010A JL 010F ;salta ao endereço 010f, se for menor 297D:010C ADD DL,07 ;Adiciona o valor 07 a DL 297D:010F INT 21 ;Chama interrupção do DOS 297D:0111 INT 20 ;Finaliza o programa Este programa imprime um valor zero em dígitos hexadecimais. Sétimo exemplo -a100 297D:0100 MOV AH,02 ;Função 2 (escreve um caractere na tela) 297D:0102 MOV DL,BL ;Põe o valor de BL em DL 297D:0104 AND DL,0F ;Transporta fazendo AND dos números bit a ;bit 297D:0107 ADD DL,30 ;Adiciona 30 a Dl 297D:010A CMP DL,3A ;Compara Dl com 3A 297D:010D JL 0112 ;Salta ao endereço 0112, se menor 297D:010F ADD DL,07 ;Adiciona 07 a DL 297D:0112 INT 21 ;Chama interrupção do DOS 297D:0114 INT 20 ;Finaliza o programa Este programa é usado para imprimir dois dígitos hexadecimais. Oitavo exemplo -a100 297D:0100 MOV AH,02 ;Função 2 (escreve um caractere na tela) 297D:0102 MOV DL,BL ;Põe o valor de BL em DL 297D:0104 MOV CL,04 ;Põe o valor 04 em CL 297D:0106 SHR DL,CL ;Desloca os 4 bits mais altos do número ao ;nibble mais à direita 297D:0108 ADD DL,30 ;Adiciona 30 a DL 297D:010B CMP DL,3A ;Compara Dl com 3A 297D:010E JL 0113 ;Salta ao endereço 0113, se menor 297D:0110 ADD DL,07 ;Adiciona 07 a DL 297D:0113 INT 21 ;Chama interrupção do DOS 297D:0115 INT 20 ;Finaliza o programa Este programa imprime o primeiro de dois dígitos hexadecimais. Nono exemplo -a100 297D:0100 MOV AH,02 ;Função 2 (escreve um caractere na tela) 297D:0102 MOV DL,BL ;Põe o valor de BL em DL 297D:0104 MOV CL,04 ;Põe o valor 04 em CL 297D:0106 SHR DL,CL ;Desloca os 4 bits mais altos do número ao ;nibble mais à direita. 297D:0108 ADD DL,30 ;Adiciona 30 a DL Terceiro passo Rodar o programa executável. Basta digitar o nome do programa criado. Exemplos de Programas Assembly Primeiro exemplo ;nome do programa: one.asm ; .model small .stack .code mov AH,1h ;Função 1 do DOS Int 21h ;lê o caracter e retorna o código ASCII ao registrador AL mov DL,AL ;move o código ASCII para o registrador DL sub DL,30h ;subtrai de 30h para converter a um dígito de 0 a 9 cmp DL,9h ;compara se o dígito está entre 0 e 9 jle digit1 ;se verdadeiro obtém o primeiro número (4 bits) sub DL,7h ;se falso, subtrai de 7h para converter a uma letra A-F digit1: mov CL,4h ;prepara para multiplicar por 16 shl DL,CL ;multiplica para converter dentro dos 4 bits mais altos int 21h ;obtém o próximo caracter sub AL,30h ;repete a operação de conversão cmp AL,9h ;compara o valor 9h com o conteúdo do registrador AL jle digit2 ;se verdadeiro, obtém o segundo dígito sub AL,7h ;se falso, subtrai de 7h digit2: add DL,AL ;adiciona o segundo dígito mov AH,4Ch ;função 4Ch do DOS (exit) Int 21h ;interrupção 21h End ;finaliza o programa Este programa lê dois caracteres e os imprime na tela Segundo exemplo ;nome do programa: two.asm .model small .stack .code PRINT_A_J PROC MOV DL,'A' ;move o caracter A para o registrador DL MOV CX,10 ;move o valor decimal 10 para o registrador CX ;este valor é usado para fazer laço com 10 interações PRINT_LOOP: CALL WRITE_CHAR ;Imprime o caracter em DL INC DL ;Incrementa o valor do registrador DL LOOP PRINT_LOOP ;Laço para imprimir 10 caracteres MOV AH,4Ch ;Função 4Ch, para sair ao DOS INT 21h ;Interrupção 21h PRINT_A_J ENDP ;Finaliza o procedimento WRITE_CHAR PROC MOV AH,2h ;Função 2h, imprime caracter INT 21h ;Imprime o caracter que está em DL RET ;Retorna o controle ao procedimento que chamou WRITE_CHAR ENDP ;Finaliza o procedimento END PRINT_A_J ;Finaliza o programa Este programa mostra os caracteres ABCDEFGHIJ na tela. Terceiro exemplo ;nome do programa: three.asm .model small .STACK .code TEST_WRITE_HEX PROC MOV DL,3Fh ;Move o valor 3Fh para o registrador DL CALL WRITE_HEX ;Chama a sub-rotina MOV AH,4CH ;Função 4Ch INT 21h ;Retorna o controle ao DOS TEST_WRITE_HEX ENDP ;Finaliza o procedimento PUBLIC WRITE_HEX ;........................................................; ;Este procedimento converte para hexadecimal o byte ; ;armazenado no registrador DL e mostra o dígito ; ;Use:WRITE_HEX_DIGIT ; ;........................................................; WRITE_HEX PROC PUSH CX ;coloca na pilha o valor do registrador CX PUSH DX ;coloca na pilha o valor do registrador DX MOV DH,DL ;move o valor do registrador DL para o registrador DH MOV CX,4 ;move o valor 4 para o registrador CX SHR DL,CL CALL WRITE_HEX_DIGIT ;mostra na tela o primeiro número hexadecimal MOV DL,DH ;move o valor do registrador DH para o registrador DL AND DL,0Fh CALL WRITE_HEX_DIGIT ;mostra na tela o segundo número hexadecimal POP DX ;retira da pilha o valor do registrador DX POP CX ;retira da pilha o valor do registrador CX RET ;Retorna o controle ao procedimento que chamou WRITE_HEX ENDP PUBLIC WRITE_HEX_DIGIT ;......................................................................; ;Este procedimento converte os 4 bits mais baixos do registrador DL ; ;para um número hexadecimal e o mostra na tela do computador ; ;Use: WRITE_CHAR ; ;......................................................................; WRITE_HEX_DIGIT PROC PUSH DX ;coloca na pilha o valor de DX CMP DL,10 ;compara se o número de bits é menor do que 10 JAE HEX_LETTER ;se não, salta para HEX_LETER ADD DL,"0" ;se sim, converte para número JMP Short WRITE_DIGIT ;escreve o caracter HEX_LETTER: ADD DL,"A"-10 ;converte um caracter para hexadecimal WRITE_DIGIT: CALL WRITE_CHAR ;imprime o caracter na tela POP DX ;Retorna o valor inicial do registrador DX ;para o registrador DL RET ;Retorna o controle ao procedimento que chamou WRITE_HEX_DIGIT ENDP PUBLIC WRITE_CHAR ;......................................................................; ;Este procedimento imprime um caracter na tela usando o D.O.S. ; ;......................................................................; WRITE_CHAR PROC PUSH AX ;Coloca na pilha o valor do registrador AX MOV AH,2 ;Função 2h INT 21h ;Interrupção 21h POP AX ;Extrai da pilha o valor de AX RET ;Retorna o controle ao procedimento que chamou WRITE_CHAR ENDP END TEST_WRITE_HEX ;Finaliza o programa Quarto exemplo ;nome do programa: four.asm .model small .stack .code TEST_WRITE_DECIMAL PROC MOV DX,12345 ;Move o valor decimal 12345 para o registrador DX CALL WRITE_DECIMAL ;Chama o procedimento MOV AH,4CH ;Função 4Ch INT 21h ;Interrupção 21h TEST_WRITE_DECIMAL ENDP ;Finaliza o procedimento PUBLIC WRITE_DECIMAL ;.................................................................; ;Este procedimento escreve um número de 16 bit como um número ; ;sem sinal em notação decimal ; ;Use: WRITE_HEX_DIGIT ; ;.................................................................; WRITE_DECIMAL PROC PUSH AX ;Põe na pilha o valor do registrador AX PUSH CX ;Põe na pilha o valor do registrador CX PUSH DX ;Põe na pilha o valor do registrador DX PUSH SI ;Põe na pilha o valor do registrador SI MOV AX,DX ;move o valor do registrador DX para AX MOV SI,10 ;move o valor 10 para o registrador SI XOR CX,CX ;zera o registrador CX NON_ZERO: XOR DX,DX ;zera o registrador CX DIV SI ;divisão entre SI PUSH DX ;Põe na pilha o valor do registrador DX INC CX ;incrementa CX OR AX,AX ;não zero JNE NON_ZERO ;salta para NON_ZERO
Docsity logo



Copyright © 2024 Ladybird Srl - Via Leonardo da Vinci 16, 10126, Torino, Italy - VAT 10816460017 - All rights reserved