Você está na página 1de 11

UNIVERSIDADE ESTADUAL DE FEIRA DE SANTANA

ENGENHARIA DE COMPUTAÇÃO

PROBLEMA I – LINGUAGEM ASSEMBLY

SISTEMAS DIGITAIS

MARCUS VINICIUS ARAUJO MARTINS


PROBLEMA I – LINGUAGEM ASSEMBLY

SISTEMAS DIGITAIS

Relatório solicitado pela


disciplina de Sistemas
Digitais, do curso de
Engenharia de Computação,
da Universidade Estadual
de Feira de Santana.

Tutor: Victor

SEMESTRE 2006.2
Março de 2007
Feira de Santana - Bahia
-2-
Sumário
1 - Introdução .............................................................................................. 4

2 – Assembly x Emu8086 ........................................................................... 5

3 - Código Asm para ordenação de um vetor .............................................. 6

4 - Conclusão .............................................................................................. 10

7 – Referências.......................................…………………………………………….. 11

-3-
Introdução

A programação computacional é minuciosa. Não por exigir a padronização e


coerência às regras estipuladas por qualquer que seja a linguagem. O fato é que, na
programação, o que se desejar fazer, deve ser planejado passo-a-passo, como numa
seqüência.
As linguagens de programação se distinguem, em especial, quanto ao nível de
linguagem computacional. Entender um computador como uma máquina multinível é a
forma mais correta de entender como as linguagens de programação são estruturadas. A
linguagem Assembly, tal qual é tratada neste relatório, se enquadra como uma linguagem de
baixo nível. Linguagens de baixo nível são certamente mais complexas para programadores,
mas tão contrário para máquinas. Uma linguagem baixa se aproxima da linguagem binária,
essa última que é realmente operada pelo computador, com base em princípios lógicos.
O programador, se tivesse o total arbítrio ao escolher uma linguagem ao programar,
certamente escolheria alguma a qual mais longe fosse da linguagem binária. Escolher uma
linguagem é possível. Entretanto, na produção de um programa, pode ser necessária certa
velocidade em determinadas execuções, por exemplo. Isso faz o programador implementar
seu programa de forma mista, concatenando uma linguagem de alto nível com uma chamada
a um bloco de procedimentos desenvolvidos numa linguagem mais baixa, a fim de resolver
seu problema. Logo, não se deve ter a idéia errônea de que uma linguagem alta é mais
moderna e, por conseqüência, mais eficiente que uma mais baixa. Ambas importam num
processo de implementação de um programa, dependendo que função ele deve satisfazer.
Esse relatório não objetiva concatenar duas ou mais linguagens num só programa.
Entretanto, ele salienta a utilização de uma linguagem, Assembly, de baixo nível, na
organização de um vetor de números em ponto flutuante. Uma explanação a respeito da
linguagem e do software utilizado para implementação do programa pode ser vista nesse
relatório. Além disso, o código escrito é comentado, salientando as decisões de projeto e os
recursos utilizados.

-4-
Assembly x Emu8086

A ferramenta pré-definida para implementação do código em Assembly foi o


emu8086. Esse software baseia-se na arquitetura do processador 8086 conceituado em 16 bits.
Quando se diz que um processador é de 16 bits, quer-se dizer que a sua unidade lógica e
aritmética, os seus registradores internos, e a maior parte das suas instruções foram
concebidos para trabalhar com palavras de 16 bits. Além disso, o 8086 tem um barramento de
dados de 16 bits, ou seja, pode ler e escrever na memória ou nas portas, 16 bits de uma só vez.
O problema proposto implica numa organização de um vetor de números em ponto
flutuante. Esse é o problema. É impreciso trabalhar com ponto flutuante no sistema 16 bits.
As estruturas montadas para efeito desse tipo de cálculo são específicas para 32 e 64 bits.
Existem três possibilidades para resolução desse problema:
A primeira seria normalizar os números. Entende-se por normalizar a converter os
números para uma determinada forma, binária, e desenvolver um certo padrão para condições
como vírgula e sinal. Para isso, usa-se representações como sinal-magitude, complemento de
dois, e a representação polarizada. Todas essas são variações da representação binária em
que, por exemplo, um bit é escolhido para designação do sinal, como é o caso da
representação sinal magnitude.
A segunda seria trabalhar com um chamado co-processador 8087. Esse seria
responsável por operar propriamente com números em ponto flutuante por se enquadrar em 32
bits. As instruções variam daquelas utilizadas para programar num código para 8086.
A terceira e, por conseqüência, a que vai ser utilizada no código, trata-se de fazer
sucessivas comparações bi-lateralmente, ou seja, comparar os valores antes e depois da
virgula, vistos como dois números inteiros. Trata-se então, de uma manipulação de valores
dentro de vetor, tendo eles 16 bits cada: oito para a parte inteira e oito para a parte fracionária.
A linguagem Assembly é dotada de uso de registradores. Estes têm funções
especificas, mas, dentro de um código, podem armazenar dados conforme queira o
programador. Além disso, funções de saltos, comparações, entre outras, incluindo
interrupções para exibição na tela ou para entrada de dados são manipuladas no código a ser
descrito e explicadas conforme o decorrer do mesmo.

-5-
Código Asm para ordenação de um vetor

A melhor forma de organização de um vetor de números, inteiros ou em ponto


flutuante, é chamado “método bolha”. Trata-se de comparar, dois a dois, todas as
possibilidades possíveis de combinação. No caso, trabalhando com três números, serão três
combinações: o primeiro numero com o segundo, o segundo com o terceiro e o primeiro com
o terceiro.
O primeiro trecho do código o nomeia. Em seguida é informado ao compilador a
partir de que ponto da memória o programa deve ser executado. A instrução “org” informa,
como no exemplo, o endereço inicial “100h”, em hexadecimal. É importante esse salto em
via de que os primeiros espaços da memória devem ficar livres para utilização do DOS.
Abaixo a instrução “org” foi utilizada uma função de salto (jmp). Ela nada mais faz
do que saltar a declaração do vetor para o bloco 1. Isso evita que na execução do código, o
vetor seja interpretado de uma forma incorreta, como uma instrução por exemplo. Outra
forma de resolver isso seria declarar o vetor, ou demais variáveis, no final de todo o código.
name "Compara ponto flutuante"
org 100h
jmp bloco1
vec db 12,51, -13,49, 14,07
bloco1:
lea si, vec
mov ah,[si]
mov al,[si+1]
lea di,vec + 2
mov bh,[di]
mov bl,[di+1]

Rótulos são utilizados quando se deseja fazer um salto direcionado ou quando se


deseja voltar a um certo ponto, por exemplo. O rótulo (ou label) “bloco1”, inicia a leitura do
vetor. É notório, na visualização da declaração do vetor, que na verdade trata-se de 6
números inteiros. Todavia, foram utilizados 2 inteiros para representar um número decimal,
conforme motivo já foi explicado. A partir disso, as leituras que forem feitas do vetor
analisam as duas partes, e logo a contagem será de dois em dois, conforme será visto.
A instrução “lea” coloca no registrador si o endereço do vetor. Quando o vetor é
declarado, ele ocupa um certo espaço na memória, a depender de seu tamanho. Logo, nessa

-6-
instrução, o endereço passado ao registrador si é o endereço do primeiro valor do vetor, que
no caso corresponde a 12. Em seguida, é movido para o registrador ah o valor ocupado no
endereço de memória que si aponta. A notação do uso dos colchetes implica que o que está
sendo passado a ah não é o endereço que si guarda, mas sim o valor que está no endereço de
memória ao qual si aponta. De igual forma, é passado para al, o segundo elemento do vetor,
que corresponde à parte decimal do primeiro valor. O incremento a si indica o deslocamento
no vetor. Tem-se então o primeiro valor em ponto flutuante armazenado. Para armazenar o
segundo, incrementou o vetor e foi usado o registrador di para apontar o endereço
correspondente. Os registradores bh e bl foram utilizados para armazenar a parte alta e baixa
do segundo valor, respectivamente.

test vec[0] , 10000000b ;


jnz negativo1
test vec[2] , 10000000b ;
jnz negativo1
cmp ah,bh
ja troca
jne bloco2
cmp al,bl
ja troca

A instrução test, na forma como foi utilizada, compara o número de vec[0], e logo
depois de vec[2] (partes inteiras dos dois primeiros números), com o número binário
10000000b. Isso funciona como uma instrução a fim de saber se o número que ocupa certo
trecho do vetor é positivo ou negativo. O primeiro bit de um número binário indica o sinal: 0
indica positivo e 1 indica negativo. A instrução então testa o número e avalia o flag “zf”. O
salto jnz só é feito caso o número testado seja negativo. Faz-se o teste com os dois números.
Se um dos dois (ou mesmo os dois), forem negativos, o programa é direcionado para um
rótulo afim de fazer a troca necessária. O rótulo foi denominado de negativo1.
Se os dois testes forem feitos e não acontecerem os saltos, isso implica que os dois
valores são positivos. Daí faz-se uma comparação entre eles. Pode acontecer três coisas: O
primeiro ser maior que o segundo, eles serem iguais, ou o primeiro menos que o segundo.
Caso a primeira situação ocorra, faz-se um salto a um rótulo chamado troca, que fará a troca
das partes inteiras e fracionárias,a partir da instrução ja (above jump- pule se maior). Se não

-7-
ocorrer, o primeiro número só pode ser igual ou menor ao segundo. Se não for igual, o
programa executa o bloco2, que agora tratará do segundo e terceiro número. A instrução jne
faz saltar se não for igual. Como só existia duas possibilidades, se não for igual só pode ser
menor então, não precisando fazer nenhuma alteração. Por fim, caso seja igual, resta
comparar a parte fracionária, fazendo a troca se necessário.
negativo1:
test vec[2],10000000b
jz bloco2
test vec[0],10000000b
jz troca
cmp ah,bh
ja troca
jne bloco2
cmp al,bl ;compara as partes fracionarias
jb troca
jmp bloco2
Entretanto, se em um dos testes o número fosse negativo, o programa passa a ser
executado no rótulo negativo1. Esse rótulo nada mais é do que algo semelhante ao rótulo
troca. Todavia, há alguns variantes. O número 2,5 é maior que 2,4, por exemplo. Todavia,
-2,5 não é menor que -2,4. Isso seria interpretado de má forma caso o rotulo chamado fosse
troca. Esse último analisaria os primeiros números. Como são iguais, analisaria o segundo,
as partes fracionárias, e faria a troca uma vez que 5 é maior que 4.
Se o programa passou a ser executado em negativo1, isso quer dizer que o primeiro
ou o segundo valor do vetor é negativo, daí testar novamente, agora dentro do bloco se o
segundo valor é negativo. Caso ele seja positivo,isso evidencia que o primeiro é negativo e,
logo, não devem ser feitas trocas entre eles e o programa passa para bloco2. Entretanto, se
for negativo, o primeiro valor é testado. Caso ele seja positivo, a troca é feita, uma vez que o
segundo valor é negativo.
Se nenhum dos testes implicar em valores positivos, isso implica que os dois valores
são negativos. Faz-se então a comparação de ah com bh. A troca é feita caso ah seja maior
que bh. Resta as opções igual ou menor. Usando a instrução jne, o salto é feito se não for
igual, logo menor, ou seja, não precisa fazer a troca. Todavia, se for igual, faz–se a
comparação de al com bl. A troca só é feita caso al seja menor que bl, uma vez que, para
negativos, funciona diferente, como já foi explicado.

-8-
troca:
push ax
mov [si],bh
mov [si+1],bl
pop bx
mov [di],bh
mov [di+1],bl
jmp bloco1

Muitas vezes o rótulo troca é chamado na execução do programa. O rótulo é descrito


acima. Por decisão de projeto, usou-se uma pilha para fazer essa mudança entre valores do
vetor. Quando algum trecho do código chama o rótulo troca, tem-se um número decimal
ocupando ah e al e outro decimal ocupando bh e bl. Faz-se um push na pilha colocando nela
o valor de ax( correspondendo a ah e al ). Como o registrador si aponta para o endereço de
memória o qual o primeiro valor do vetor, por exemplo, está ocupando, move-se o conteúdo
de bh e bl para as posições de memória apontadas por si e seu incremento, respectivamente,
colocando assim a parte inteira e fracionária no espaço de memória que antes pertencia ao
segundo valor do vetor. Fazendo um pop para bx, o valor de ax passa para bx, que agora é
movido para o endereço de memória que o registrador di aponta. Além desses rótulos, no
código também existem bloco2 , bloco3, negativo2 e negativo3. Todavia, esses são
repetições dos blocos apresentados, uma vez que agora vão trabalhar de igual fora com as
outras combinações possíveis para os valores do vetor.

-9-
Conclusão

Trabalhar com números em ponto flutuante é uma tarefa de precisão. Esse código em
Assembly não abusou da precisão uma vez que operou sob 16 bits: oito para a parte inteira e
oito para a parte fracionária. Ou seja, existem 256 possibilidades de números para cada. Em
contrapartida, se fosse trabalhado com 32 bits, as possibilidades aumentariam de 256 para
65536. Mudaria algumas coisas no código como ,por exemplo, ao contrário de usarmos ah e
al, para armazenar um número decimal, usaríamos ax e bx, uma vez que cada um deles
corresponde a 16 bits.
A outra forma de trabalhar com 32 bits seria usar um co-processador 8087, conforme
já foi dito no decorrer do relatório. Esse co-processador permitiria a entrada direta com
números em ponto flutuante. Sob alguns tipos de instruções diferentes, um programa
desenvolvido para esse co-processador trabalha os valores de um vetor desordenado de
números numa pilha de registradores de 32 bits, que vai de st0 a st7, logo oito registradores.
Os valores do vetor poderiam ser transpostos para essa pilha e, assim, o código executável,
acompanhado da diretiva #fasm#, que faz com que o compilador entenda que os valores
estão em ponto flutuante, faria a ordenação do vetor.
Quando se opera com números positivos e negativos no mesmo vetor, as 256
possibilidades de números se dividem: de -127 a 128, ou -128 a 127. O número -2, por
exemplo, corresponde a 254, caso ele seja visto como um inteiro sem sinal. Isso pode
complicar a execução do código caso, por exemplo, os valores -255,12 e 238,40 estejam
presentes no vetor. A diferença entre eles é um número consideravelmente maior que 256.
A linguagem Assembly não é uma linguagem de alto nível. Sua codificação não é
simples e nem tão pouco sugestiva. Todavia, trabalhar com linguagens de baixo nível
diminui as instruções e torna as execuções mais práticas e rápidas. Daí programas serem
montados de forma mista, concatenando duas ou mais linguagens. É válido lembrar ainda
que, no processo de compilação de um programa de alto nível, o compilador se incube de
codificar as instruções para linguagens mais baixas, até alcançar a mais baixa possível, a
linguagem binária. Uma compilação de um programa em linguagem C, por exemplo, gera
um código referente em Assembly enquanto não alcança a forma binária.

- 10 -
Referências

1- TOCCI, Ronald J.; WIDMER, Neal S. Sistemas digitais : principios e aplicacoes. 7.


ed Sao Paulo: LTC.

2- IDOETA, Ivan V. (Ivan Valeije); CAPUANO, Francisco G. (Francisco Gabriel).


Elementos de eletronica digital. 11. ed Sao Paulo: Erica, 1986.

3 – DETMER, Richard C. Introduction to 80×86 Assembly Language and


Computer Architecture

4 –http://www.mikroelektronika.co.yu/portuguese/product/books/picbook/capitulo4.htm

- 11 -

Você também pode gostar