Assembly

€

Linguagem Assembly é uma linguagem de programação de baixo nível. Necessitamos de obter algum conhecimento sobre a estrutura do computador. O funcionamento simples do computador é como o esquema seguinte:

€

O sistema de BUS (mostrado em amarelo) conecta / liga os diferentes componentes de um computador. O CPU é o coração do computador, a maioria dos cálculos ocorrem no interior da CPU. A memória RAM é o local onde os programas são carregados, a fim de ser executados.

€

€

O 8080 é um processador de 8 bits, isto é, a sua UAL opera sobre números de 8 bits. O seu bus de sistema tem, também, 8 linhas de dados. O 8086 é um processador de 16 bits, com uma UAL operando sobre números de 16 bits, pelo que o seu bus interno ao CPU e os seus registadores do CPU são de 16 bits. De modo a manter alguma compatibilidade, contudo, os registadores de dados do 8086 podem ser manipulados em 'duas metades' de 8 bits cada, de modo a, assim, se poder fazer a correspondência com os registadores de 8 bits, do 8080. A compatibilidade entre estes dois processadores não vai muito mais longe, já que no 8086 se pretendeu alcançar um espaço de endereços reais de 1MegaByte, muito superior aos 64KBytes do 8085, e a própria forma de endereçar a memória é no 8086, muito mais sofisticada, pois recorre à técnica de segmentação, que o 8085 não tinha.

€

€

€

.

€ Unidade Central de Processamento .

€ O CPU 8086 tinha 8 registos genéricos. CX ² o contador de registos (dividido em CH / CL). BX .o registo do endereço base (dividido BH / BL).registo de destino de index. SP ² ponteiro para a pilha. SI ² registo de procura de index. € € € € € € € . BP ² ponteiro para a base. DI . DX ² o registo de dados (dividido em DH / DL). cada registo tinha o seu próprio nome: AX ² o acumulador de registos (dividido em AH / AL).

ou seja: 0011000000111001b (na forma binária). "H" significa "Alto". O principal objectivo de um registo é guardar um número (variável). O mesmo acontece para os outros 3 registos. se AX = 0011000000111001b o AH=00110000b e AL= 00111001b. o registo mais significativo e "L" significa "Baixo" ou a parte do registo menos significativa. CX. € . DX) podendo considerar-se dois registos de 8 bits. O tamanho dos registos é de 16 bits. No entanto quando se modifica qualquer um dos registos de 8 bits o registo de 16 bits é actualizado. ou 12345 em decimal (escrita utilizada pelo homem). e vice-versa. O microprocessador 8086 tem 4 registos genéricos (AX. por exemplo. BX.€ € A utilização de cada registo é determinada pelo utilizador.

€ Como os registos estão localizados no interior da UCP são muito mais rápidos do que a memória. € . Portanto. O acesso aos dados num registo normalmente não tem tempo. mas continuam a ser um bom lugar para armazenar temporariamente os dados que resultam dos cálculos. leva muito mais tempo. por isso. o utilizador deve tentar manter as variáveis nos registos. Aceder a uma memória local requer a utilização de um sistema de barramento (bus). O conjunto de registos é muito pequeno e a maior parte deles têm funções especiais que limitam o seu uso como variáveis.

têm a função de apontar para os blocos de memória acessíveis. SS ² aponta para o segmento que contem a pilha. Embora seja possível armazenar alguns dados nos registos do segmento. Os registos do segmento trabalham juntamente com os registos genéricos. Os registos do segmento têm uma função muito especial. DS ² geralmente aponta no segmento onde as variáveis estão definidas. para aceder a qualquer valor da memória. o código é actualizado sempre que um registo de segmento é utilizado.€ € € CS ² aponta para o segmento que contem o programa actual. esta hipótese não é a melhor. Por exemplo se o utilizador precisar de aceder à memória no endereço físico 12345h (hexadecimal). ES . multiplicando o registo de segmento por 10h e adicionando o registo genérico a este (1230h * 10h + 45h = 12345h): € € € € € . O processador (CPU) faz um cálculo do endereço físico.registo de segmento extra. deverá definir o DS = 1230h e o SI = 0045h. Desta forma tem-se acesso a muito mais memória do que com um único registo que está limitado a 16 bits. ou seja.

BH e BL não o podem fazer. Por defeito. os registos BX. . O BP e o SP trabalham com o registo de segmento SS.€ € € € € O endereço formado com 2 registos. Outros registos com funções gerais não podem formar um endereço efectivo! Embora BX possa formar um endereço eficaz. tem o nome de endereço efectivo. SI e DI trabalham com registo de segmento DS.

a forma como nós podemos aceder a estes registos é através do Ax e outros registos genéricos. . O registo das Flags é modificado automaticamente pelo CPU depois de qualquer operação matemática. Normalmente não podemos ter acesso a esses registos directamente. Flags Register .determina o estado actual do microprocessador. O registo IP trabalha sempre com o CS que é um registo de segmento que aponta para a instrução que no momento está a ser executada. mas é possível alterar valores nos registos de sistema usando alguns truques.o ponteiro de instrução.IP . e determinar as condições de transferência de controlo para outras partes do programa. isto permite determinar o tipo de resultado.

Combinando estes registos dentro de parêntesis rectos. DI. SI. BP.€ Para ter acesso à memória pode-se usar estes quatro registos: BX. pode-se obter diferentes locais na memória. Estas combinações suportam: .

€ O deslocamento pode ser um valor imediato ou uma variável. O deslocamento é um valor assumido. Se forem vários valores. o assembly avalia todos os valores e calcula um valor imediato simples.Mantêm-se em 16 bit designado por deslocamento imediato. por isso pode ser positivo ou negativo. O deslocamento pode estar dentro ou fora de parêntesis recto. ou mesmo ambas. .€ € D8 ² Mantêm-se em 8 bits designado por deslocamento imediato. Geralmente o compilador tem atenção à diferença entre o d8 e d16 e gera o código máquina necessário. D16 . e o assembly gera o mesmo código máquina para ambos.

BX = 30. Por defeito os registos de segmentos DS são usados para todos os modelos. vamos assumir que DS = 100. excepto aqueles com registos BP.€ € € Por exemplo. SI = 70 O seguinte endereço: [BX + SI] + 25 é calculado pelo processador para este endereço físico : 100 * 16 + 30 + 70 + 25 = 1725. Existe uma maneira fácil de recordar todas estas possíveis combinações usando este quadro: € € . para estes registos de segmentos é usado o SS.

€ € Pode-se formar todas as combinações válidas ao retirar apenas um item de cada coluna. O endereço físico pode ser 1234h * 10h + 7890h = 19BD0h. então se zero for adicionado a um valor hexadecimal. e o propósito do valor do registo (BX.. SI. Se zero for adicionado a um número decimal é multiplicado por 10. O valor no registo de segmento (CS. Aqui está um exemplo de um modo de endereço válido:[BX+5]. DI. Como se pode verificar BX e BP nunca se encontram juntos. Para SI e DI isto também é válido. Quando DS contem o valor de 1234h e SI contem o valor 7890h ele também pode ser guardado como 1234:7890. ou não retirando nenhum item da coluna. SS.[DI+BX-4]. BP) é chamado de offset. DS.[BX+SI]. contudo 10h = 16. ele é multiplicado por 16. por exemplo: 7h=7 70h=112 € € € . ES) é chamado de segmento.

. estes são os prefixos que devem ser utilizados: · byte ptr . O assembler também suporta pequenos prefixos tais como: · b.para byte ptr · w. byte access.para word ptr Em certos casos o assembler pode calcular o tipo de dados automaticamente. . word access.€ Para falar acerca do tipo de dados do compilador. Por exemplo: byte ptr [BX] . or word ptr [BX] . . · word ptr ² para palavras (dois bytes).para bytes.

DX. etc. DL. BL. immediate REG: AX. BP. Ambos os operandos têm de ter o mesmo tamanho. 3Fh. que pode ser um byte ou uma palavra.. memory MOV memory. O operando fonte pode ser um valor imediato. DH. O registo de destino pode ser um registo genérico ou uma localização na memória. REG MOV REG. São suportadas estes tipos de operações: MOV REG. CX.. variable. SP. -24. memory: [BX].. BX. immediate: 5. 10001101b.. AH. BH. . SI.€ € € € € Copia o segundo operando (fonte) para o primeiro operando (destino). etc. CL. um registo genérico ou a localização na memória. DI. [BX+SI+7]. REG MOV memory. immediate MOV REG. AL. CH.

BX. SI. CH. SREG MOV REG.€ Para registos de segmentos apenas estes tipos de MOV são suportados: MOV SREG. SP. etc.. memory MOV memory. [BX+SI+7]. AL. BP. A instrução MOV não pode ser usada para colocar o valor nos registos CS e IP. ES. variable. REG SREG: DS. € . REG: AX. AH. CL. and only as second operand: CS. SS.. DL. BL. DX. DI. DH. memory: [BX]. SREG MOV SREG. BH. CX.

15Eh . AX . it is 41h. set CH to binary value. MOV BX. MOV CL. MOV [BX]. set CL to ASCII code of 'A'. CX . MOV DS. 1101_1111b . 0B800h . copiando e colando seguido de F5 . this directive required for a simple 1 segment . 'A' .> comentários . MOV AX. copy contents of CX to memory at B800:015E RET . set BX to 15Eh. . copy value of AX to DS.com program. returns to operating system.> single step . MOV CH.€ Pequeno programa que demonstra o uso da instrução MOV: ORG 100h . set AX to hexadecimal value of B800h. € € Utilizando um emulador .

Para o programador é muito mais fácil ter um valor guardado numa variável chamada ´var1µ do que no endereço chamado 5A73:235B. Este compilador (8086) suporta dois tipos de variáveis: BYTE e WORD. A variável está num local da memória. .€ O programa anterior escreve directamente para a memória de vídeo. especialmente quando se tem 10 ou mais variáveis. DW ² Mantém-se em Define Word. Sintaxe para a declaração de variável: name DB value name DW value DB ² Mantém-se em Define Byte. portanto pode-se ver que MOV é uma importante instrução.

var2 RET . value ² Pode ser qualquer valor numérico de qualquer sistema de numeração (hexadecimal. ou decimal). € A instrução MOV também é utilizada para copiar valores de origem para o destino. stops the program. ou "?" símbolo para as variáveis que não são inicializadas.€ name ² Pode ser qualquer combinação de letras ou dígitos. VAR1 DB 7 var2 DW 1234h . binário. Exemplo com a instrução MOV: ORG 100h MOV AL. var1 MOV BX. deve começar com uma letra. É possível declarar variáveis sem nome não especificando o nome (Esta variável terá um endereço mas não um nome).

deverá obter-se o seguinte : .€ Ao copiar este código para um emulador do processador 8086.

a segunda linha é um valor hexadecimal. Na lista da memória a primeira linha é um offset. a terceira linha é um valor decimal. e a última linha é um carácter da tabela ASCII € O compilador não distingue letras maiúsculas das minúsculas. automaticamente repõe todas as variáveis com os respectivos nomes e offsets. O offset de VAR1 é 0108h. e o endereço completo é 0B56:0108. Quando o compilador fizer o código máquina. por isso "VAR1" e "var1" referem-se à mesma variável. .€ Como se pode ver parece-se com este exemplo. excepto as variáveis que são repostas com os locais da memória actualizados. Por defeito o segmento é carregado no registo DS (Quando os ficheiros COM são carregados o valor do registo DS está definido para o mesmo registo CS).

Assume-se que o byte menos significativo é armazenado no endereço menos significativo. por isso 34h está localizado antes de 12h. e só processar os valores na memória e percebê-los como uma instrução válida do 8086.€ € € O offset de var2 é 0109h. só uma directiva para fazer um ficheiro simples DB 0A0h DB 08h DB 01h DB 8Bh DB 1Eh DB 09h DB 01h DB 0C3h DB 7 DB 34h DB 12h . Como se pode ver. esta variável é uma WORD por isso ocupa 2 bytes. e o endereço completo é 0B56:0109. existem algumas outras instruções depois da instrução RET. Pode-se escrever o mesmo programa utilizando só a mesma directiva DB: ORG 100h . isto acontece por causa do descompilador não saber onde começam os dados.

Os directivos nunca são convertidos para o código máquina real. O ficheiro executável indica ao compilador como vai carregado no offset 100h (256 bytes). o compilador só converte o programa fonte para o conjunto de bytes.€ Como se deve imaginar. Embora isto seja verdade só para os ficheiros COM. ORG 100h é um compilador directivo (significa que o compilador diz como trabalhar com o código fonte). os ficheiros EXE são carregados no offset em 0000. geralmente são usados segmentos para variáveis. parâmetros etc. Este directivo é muito importante quando se trabalha com variáveis. tais como linha de comandos. este conjunto tem o nome de código máquina. por isso o compilador calcula o endereço correcto para todas as variáveis quando repõe os nomes das variáveis com os offsets. Por que é que os ficheiros executáveis são carregados no offset com 100h? O sistema operativo mantém alguns dados do programa nos primeiros 256 bytes do CS (Código segmento). e o processador percebe e código máquina e executa-o. € .

quando compilado vê-se uma string dentro de aspas automaticamente converte-a num conjunto de bytes. 6Ch. 00h b DB 'Hello'. 6Ch. Uma string de texto pode ser vista como um exemplo de byte array. cada carácter é apresentado como um código ASCII (0. A figura abaixo mostra uma parte da memória onde estes arrays são declarados: .€ Arrays Arrays podem ser vistos como cadeias de variáveis. 6Fh. Aqui ficam alguns exemplos de definições de array: a DB 48h.. 65h. 0 b é uma cópia exacta do a array.255).

por exemplo: MOV AL. 9. SI.número de duplicar a fazer (qualquer valor constante). 9 . a[3] Podemos também usar uma qualquer utilização dos registos da memória índice BX. 3 MOV AL. value . 9. BP . A sintaxe para o DUP é: number DUP ( value(s) ) number . 9.€ Pode-se ter acesso ao valor de qualquer elemento do array usando parentesis rectos. a[SI] € Se precisarmos de declarar um array maior . por exemplo: MOV SI. DI.DUP expressão que será duplicado. Para exemplo: c DB 5 DUP(9) É uma alternativa a forma de declarar: c DB 9. podemos usar o operador DUP.

por exemplo. € € . Tanto a instrução OFFSET como a LEA podem ser usadas para obter o endereço da variável offset. Dw não pode ser usado para declarar strings. pois permite que o utilizador obtenha o endereço de uma variável indexada. quando o utilizador precisa de passar parâmetros para um procedimento. A LEA é a melhor. Obter o endereço da variável pode ser muito útil nalgumas situações.€ Podemos usar DW em vez do DB se for necessário guardar valores maiores de 255 ou mais pequenos que ² 128. OBTER O ENDEREÇO DA VARIÁVEL Obtendo o endereço da variável Há uma instrução LEA (Endereço efectivo de carregamento) e um operador alternativo OFFSET.

. Prefixos mais curtos: b. os seguintes prefixos devem ser utilizados: BYTE PTR ² por byte. . . WORD PTR . Por exemplo: BYTE PTR [BX] . palavra.para BYTE PTR w.para WORD PTR O assembler poderá ás vezes calcular o tipo de dados automaticamente. Byte.por palavra (dois bytes).€ Temos de informar ao compilador o tipo de dados. ou WORD PTR [BX] .

€ Exemplo .

que usa o OFFSET em vez do LEA: € Os exemplos tema a mesma finalidade .€ Exempo.

€ Estas linhas: LEA BX. SI. var1 MOV BX. Depois da definição da constante o seu valor não pode ser mudado. Por favor note que estes registos só podem ser utilizados dentro de parênteses (como a memória dos ponteiros): BX. OFFSET var1 São ainda compiladas no mesmo código máquina: MOV BX. Para definir a constante EQU é utilizado o directório: name EQU < any expression > é similar a MOV AX . BP! € Constantes As constantes são como as variáveis. num num é um valor de 16 bits da variável offset. 5 . mas existem apenas até o programa estar compilado (assembler). DI.

. SIGNED ² decimal designado (base 10) . CHAR ² Carácter ACII (existem 256 símbolos. A variável pode ser vista em qualquer sistema de numeração: . HEX ² hexadecimal (base 16) . UNSIGNED ² decimal indefinido . BIN ² binário (base 2) . através do menu ´Viewµ. alguns são invisíveis).€ Podemos ver as variáveis enquanto o programa é executado. seleccionando ´variablesµ. OCT ² octal (base 8) .

4. por exemplo: 5+2 (isto vai ser convertido para 7). .3.€ É possível por números em qualquer sistema. As expressões são automaticamente convertidas. Os Arrays podem ser postos desta maneira: 1.5 (o array pode ser array de bytes ou word. binário sufixo ´bµ e octadecimal sufixo ´oµ.0 ( esta string termina em zero). números hexadecimais devem ter o sufixo ´hµ. dependendo num entanto se a variável seleccionada for BYTE ou WORD. A string pode ser posta desta maneira: ´Hello world .2. Os números decimais não requerem sufixo.

existem interruptores que trabalham com funções com o disco rígido e outros hardware. um número entre 0 a 255 (ou 0 para 0FF/h). Os interruptores também são desencadeados por diferentes tipos de hardware. A estas funções chamamos de interruptores de software. em vez de escrever um código para imprimir um caracter que se pode ligar a um interruptor. tem a seguinte sintaxe: € INT valor Sempre que um valor seja. utiliza-se números hexadecimais. Também. Estas funções tornam a programação muito mais fácil pois. geralmente. Para fazer com que haja um interruptor de software numa instrução INT.€ Os interruptores podem ser vistos como uma série de funções. é este que vai fazer tudo. . a estes são chamados de interruptores de hardware.

mas às vezes usamse outros registos. antes de ser definido. Cada interrupção pode ter até 256 sub-funções (por isso. Cada interrupção pode ter várias sub-funções. deve-se chamar o interruptor. Para especificar uma função de registo sub-AH. O exemplo seguinte usa INT 10h sub-tipo de função 0Eh é uma mensagem do tipo "Olá!". esta função exibe uma personagem. mas não é correcto.€ Pode-se pensar que existem apenas 256 funções. Quando é necessário. os outros registos são usados para passar parâmetros de dados e para a sub-função. € . ficamos com 256 * 256 = 65536 funções). Geralmente. que promove o cursor e o deslocamento no ecrã. Em geral é utilizado o AH.

.

linha . O compilador pesquisa automaticamente para o arquivo na mesma pasta onde está localizado o arquivo de origem.desactiva o cursor de texto. define a posição do cursor.€ Para tornar a programação mais fácil. Para utilizar qualquer das funções em emu8086. CURSOROFF .macro com 1 parâmetro.procura na pasta Inc.inc deve-se ter a seguinte linha no início do seu arquivo de origem: inclued 'emu8086. CURSORON ² activa o cursor de texto.inc " emu8086.inc define as seguintes macros: PUTC char . imprime a uma string. imprime uma saída em caracteres ASCII com a posição actual do cursor. imprime uma string. PRINTN string . GOTOXY col. Para o programa usar funções definidas noutro arquivo deve-se usar a directiva INCLUDE seguida por um nome de arquivo. há algumas funções comuns que podem ser incluídas no programa. e se não encontrar o arquivo . PRINT string .macro com 2 parâmetros. O mesmo que PRINT mas adiciona automaticamente "carriage return" no fim da string.macro com 1 parâmetro.macro com 1 parâmetro. € € .

pode tornar o executável demasiado grande (os procedimentos são melhores para a optimização do tamanho). usar a instrução CALL seguida pelo nome do procedimento. Por exemplo: .inc também define os seguintes procedimentos: Para usar qualquer um dos procedimentos abaixo mencionados deve-se primeiro declarar a função no final do seu arquivo (mas antes do fim da instrução) e. como por exemplo: Quando o compilador processar o código fonte vai pesquisar o arquivo emu8086.€ Para usar qualquer uma das macros acima basta digitar o seu nome nalgum lugar do seu código e. se necessário parâmetros. em seguida. € € € Procedimentos O emu8086. Geralmente as macros são relativamente pequenas partes de código.inc para as declarações dos macros e substitui os nomes da macro com o código real. O uso frequente de uma macro.

você ainda tem um executável com um tamanho bastante pequeno.€ € € € Primeiro o compilador processa as declarações (estas vão apenas regular as macros que são expandidas para procedimentos). uma vez que mesmo que você chame o mesmo procedimento 100 vezes o seu código. Isto é bastante útil. . Quando a instrução CALL é executada o controlo é transferido para o procedimento. Quando o compilador chega à instrução CALL substitui o nome com o endereço do código onde o procedimento é declarado.

procedimento para obter uma string nula obtido a partir de um utilizador. O processo pára quando a entrada "Enter" é pressionada. a string recebida é escrita para o Buffer DS: DI. Para usá-lo. Para usá-lo. A string ZERO TERMINATED deve ser definida logo após a instrução CALL. . Por exemplo: CONVITE PTHIS db 'Olá Mundo! ". declarar: DEFINE_PRINT_STRING antes da instrução END. GET_STRING .procedimento para imprimir uma string nula arquivado cursor na posição actual (tal como PRINT_STRING).procedimento para imprimir uma string nula arquivada pela posição actual do cursor. mas recebe o endereço da string de pilha. o tamanho do Buffer deve ser no DX.€ PRINT_STRING . recebe o endereço da string no registo DS: SI. PTHIS . 0 Para usá-lo declarar: DEFINE_PTHIS antes da instrução END. declarar: DEFINE_GET_STRING antes da instrução END.

(feito percorrendo toda a janela do ecrã). Para usá-lo. declarar: DEFINE_CLEAR_SCREEN antes da instrução END. Para usá-lo. PRINT_NUM_UNS . declarar: DEFINE_PRINT_NUM_UNS antes da instrução END. e definir o cursor para o topo dele. e armazena o resultado no registo CX. o número obtido a partir do teclado. SCAN_NUM .procedimento para limpar o ecrã. Para usá-lo. declarar: DEFINE_SCAN_NUM antes da instrução END. . PRINT_NUM . declarar: DEFINE_PRINT_NUM e DEFINE_PRINT_NUM_UNS antes da instrução END.procedimento que imprime um número não registado no registo AX.procedimento que recebe o multi-dígito.procedimento que imprime um número designado no registo AX.€ CLEAR_SCREEN . Para usá-lo.

€ EMU8086 .

Sign up to vote on this title
UsefulNot useful