OBS:Poderá postar suas dúvidas no fórum sobre assembly da UOL.

 

 

Instruções de Adição

 

 

 

Nesse ponto será visto as instruções de Adição do 8088, que são apresentadas nesta tabela*:

 

ADIÇÃO
ADD Adicionar byte ou palavra
ADC Adicionar byte ou palavra com transporte (carry)
INC Incrementar byte ou palavra de 1
AAA Ajuste ASCII na adição
DAA Ajuste decimal na adição

*Quando falo de "byte ou palavra", refiro-me a dados de 8 e 16 bits respectivamente.

 

++ADD++

Agora basta ir por partes. Veja como ADD pode ser usada:

MOV    AX, 32H    ;AX=32h="2" (em decimal)

MOV    BX, 35H    ;BX=35h="5" ( em decimal)

 

ADD    AX, BX     ;ADD significa, some AX com BX e salve a soma em 

                                ;AX

 

;

;Nesse ponto AX=67h

;

 

 

Outro exemplo:

 

MOV    AX, 32H    ;AX = 32H

ADD    AX, 35H    ;AX = AX + 35H  

;

;Nesse ponto AX = 32h + 35h >>> AX = 67h

;

 

Note que o valor anterior de ADD é salvo em AX, o operando destino. É como se o ADD tivesse uma instrução MOV embutida. Para comprovar veja:

 

MOV    CX, 20    ;CX=20

ADD    CX, 10    ;CX=30  

ADD    CX, -3     ;CX=27

ADD    CX, -15   ;CX=12

ADD    CX, 50    ;CX=62

 

MOV    soma, CX    ;CX=62=soma

 

 

Quais os flags que ADD afeta? São eles: OF, SF, ZF, AF, PF e CF. Que tal saber qual será o estado dos flags, supondo que antes da instrução "MOV" eles estavam resetados (iguais a zero) ?

 

MOV    AL, 35

ADD    AL, 50

 

;Já sabe quais ?

 

 

Digamos que seja feita essa soma:

 

MOV    AX, 65000

MOV    CX, 3200

ADD    AX, CX

 

Qual valor estará em AX, se o seu máximo é 65535 ? A explicação é a seguinte: Como o máximo valor dum registro pode ser 65535 (na realidade 65536 contando com o zero), é feita uma subtração por 65536. Na verdade são subtrações sucessivas até que o valor seja menor igual a 65536.

 

Como AX=68200 basta fazer: 68200 - 65536=2664 e pronto, este é o valor contido em AX. Naturalmente o flag de overflow foi para um (OF=1), indicando que houve excesso. 

 

 

 

 

++ADC++

 

 

"Qual a diferença de ADD para ADC ?" Praticamente nenhuma, pois ambos somam da mesma forma. Mas ADC leva em consideração o flag de carry. Se este estiver  setado (CF=1) é feita uma soma com "vai um". Esse "vai um" quer dizer que o limite de armazenamento do registrador ou variável foi ultrapassado. Por exemplo: Se você usar um registro de 8 bits para fazer a seguinte soma:

 

MOV    AL, 200

ADD    AL, 130

 

Nesse momento o limite de 8 bits de AL foi ultrapassado (pois passou o valor decimal de 255), então o flag de carry será setado (CF=1). Digamos agora que queira mostrar esse valor na tela e faça:

 

MOV    CL, AL

 

MOV    AH, 2

MOV    DL, CL

INT       21H

 

Na tela, iria aparecer a letra "J". "Mas como, se AL=330 ?", talvez se pergunte. Como o código ASCII vai até 255 (na realidade 256 contando com o zero) é feita uma subtração por 256. Na verdade são subtrações sucessivas até que o valor seja menor igual a 255.

 

Como AL=330, basta fazer 330 - 256=74 e pronto, este é o caractere mostrado na tela.

 

Digamos agora que AL=330 e você faça mais uma soma:

 

MOV    AL, 130

ADD    AL,  200

 

;CF=1

 

ADD    AL,  70

 

MOV    CL, AL

 

MOV    AH, 2

MOV    DL, CL

INT       21H

 

 

Qual valor será mostrado na tela ? É fácil:

 

AL = 330 + 70 = 400

CL = 400

 

Na tela = 400 - 256 = 144

 

Na tela será mostrado o caractere de número 144 (144 = É). Mas esse valor é incorreto, pois não foi levado em consideração o flag de carry, CF. Se a soma fosse feita levando-se em conta o CF, o resultado seria 145 (145 = æ).

 

Para provar veja:

 

MOV    AL, 130

ADD    AL,  200

 

;CF=1

 

ADC    AL,  70

 

MOV    CL, AL

 

MOV    AH, 2

MOV    DL, CL

INT       21H

 

Qual valor será mostrado na tela ? É fácil:

 

AL = 330 + 70  = 400

CL = 400

 

Na tela = 400 - 256 = 144

Como CF=1, basta somar 1

 

Na tela = 144 + "vai um" = 144 + 1 = 145

 

Pra ser sincero, nem nos meus livros de referência encontrei uma explicação dessas. É por isso levo mais de 15 dias para atualizar o site, pois quero informações completas além de exemplos para cada instrução apresentada.

 

OBS: Estes exemplos foram testados usando um registro de 8 bits. Se usar um maior, naturalmente, o flag de carry só será setado (CF=1)  se o limite de armazenamento dele for ultrapassado.

 

Quais os flags que ADC afeta? São eles: OF, SF, ZF, AF, PF e CF

 

 

 

++INC++

 

O que INC (Increment)  faz é acrescentar 1 ao conteúdo de um registrador ou variável, trabalhando com operandos de 8 e 16 bits. A instrução INC atualiza  os flags  AF, OF, PF, SF e ZF. O flag CF não é afetado. A lógica de INC é:

 

contador = contador + 1

 

Sintaxe:

 

INC    registrador ou variável 

 

 

 

 

++AAA++

 

Antes seria bom sabermos a diferença entre os formatos decimal compactado e não-compactado.  No formato decimal não-compactado, cada nibble (grupo de 4 bits) representa 1 dígito e no compactado cada 2 nibbles (grupo de 8 bits) representam 1 dígito.

 

A instrução AAA (ASCII Adjust for Addition) altera o conteúdo do registrador AL de um formato compactado para um não-compactado.

 

A sua lógica é:

 

SE (AL AND 0F) > 9 ou AF=1 então:

AL=AL+1

AH=AH+1

AF=1

CF=AF

 

Por causa de AF seria prudente zerar o flag de carry auxiliar(AF) antes de se usar AAA. Para zera-lo faça:

 

XOR    AH, AH    ;ou senão "MOV    AH, 0"

SAHF                   ;SAHF significa: armazenar registro AH nos flags

 

Sendo que os flags afetados são: SF, ZF, AF, PF e CF. Esta tabela representa a posição relativa dos flags em AH.

 

7 6 5 4 3 2 1 0 Bits de AH
SF ZF - AF - PF - CF Flags

 

Mas com isso você acaba de zerar TODOS os flags. Se quiser zerar apenas AF faça o seguinte:

 

MOV      AH, 11101111b    ; só altera AF

SAHF

 

"Se CF estava antes com 0, esta alteração dos flags é correta ?" Boa pergunta! pois as linhas acima não levam em consideração o resultado anterior dos flags. Então, qual a lógica correta? Veja:

 

LAHF         ;salva flags em AH

AND    AH, 1110 1111b    ;altera-se apenas AF, pois 1 AND alguma_coisa é

SAHF                                   ;sempre alguma_coisa.

                                              ;armazena AH nos flags.

 

Agora sim, estamos agindo corretamente, e o resultado anterior dos flags é preservado. É por causa disso que que as instruções de ajuste devem ser usadas logo depois das instruções aritméticas, pois o estado dos flags pode ser alterado, resultando assim numa operação incorreta.

 

Sem mais, veja um exemplo do uso de AAA:

 

MOV    AL, "9"    ; AL="9"

ADD    AL, "4"    ;AL= "9" + "4"

 

MOV    CL, AL

 

MOV    AH, 2

MOV    DL, CL

INT      21H

 

Qual caractere será mostrado na tela ? Veja:

 

AL = "9" + "4"

AL = 39H + 34H

AL = 6DH

 

Na tela = 6DH, onde 6DH é igual a letra "m".

 

"Mas como transformar 6DH="m" no número '13' ?" Eis a utilidade de AAA:

 

MOV    AL,"9"    ; AL="9"

ADD    AL, "4"    ;AL="9" + "4"

AAA

 

MOV    CL, AL

 

MOV    AH, 2

MOV    DL, CL

INT      21H

 

Qual caractere será mostrado na tela ? Veja:

 

AL = "9" + "4"

AL = 39H + 34H

AL = 6DH

 

A lógica da instrução AAA é:

 

Se (AL AND 0Fh) > 9 ou AF=1 então:

AL=AL+6

AH=AH+1

AF=1

CF=AF

 

Teremos então:

 

AL=6Dh

AL AND 0Fh = 

6Dh AND 0Fh = 

0Dh=AL

 

Como 0Dh > 9 segue-se a lógica:

AL=0Dh + 6

AL= 13h

 

Poderia-se esperar esse resultado (AL=13h), mas na prática isso não é verdade. O raciocínio está certo, mas usando-se AAA apenas os 4 últimos bits serão usados, os outros irão virar 0's ou seja, serão resetados. Dessa forma sobra apenas o número 03h em AL.

 

Já em AH tem-se a seguinte lógica:

 

AH=AH+1

 

Se AH=0 antes de se usar AAA, teremos:

 

AH=0+1

AH=1

 

Em AX, portanto teremos o número 0103h, que com um pouco de "imaginação" pode virar o número "13" decimal, pois AH=1 e AL=3.

 

Justamente o número 13! Só resta agora mostrar o número "13" na tela. Veja uma forma de se fazer isso:

 

- Voltando ao código tem-se:

 

XOR    AX, AX

MOV    AL,"9"    ; AL="9"

ADD    AL, "4"    ;AL="9" + "4"

AAA

 

ADD    AX, 3030h    ;o mesmo que: ADD  AH, 30h e ADD   AL, 30h

 

MOV    dez, AH

MOV    uni, AL

 

MOV    AH,2    

MOV    DL,0

MOV    DH,0    ;acho que sabe o que é isso ?!

MOV    BX,0

INT    10H

 

MOV    AH, 2

MOV    DL, dez

INT      21H

 

MOV    AH,2

MOV    DL,2

MOV    DH,0    ;acho que sabe o que é isso ?!

MOV    BX,0

INT    10H

 

MOV    AH, 2

MOV    DL, uni

INT      21H

 

 

 

++DAA++

 

 

A instrução DAA (Decimal Adjsut for Addition) faz um ajuste decimal em dois operandos no formato compactado. Ou seja, 2 dígitos decimais em só byte.

 

Agora vejamos como funciona DAA. Lembre-se que a sua lógica é a seguinte:

 

SE (AL AND 0Fh) > 9 ou AF=1 então

AL=AL+1

AF=1

 

SE AL > 153 ou CF=1 então

AL=AL+ 102

CF=1

 

Note que CF=1, isso indica que o limite de 153 decimal (ou 99h) em AL foi ultrapassado. Quando isso ocorre, soma-se o conteúdo de AL com 102. Por exemplo: Digamos que seja feita a soma de 85h com 50h.

 

85h+50h= D5h

Usando-se DAA, ficaria assim:

 

85h+50h=D5h

como AL=D5h então AL>99h 

AL=AL+102*, onde 102 decimal=66h

AL=D5h+66*h

AL= 13B

AL=13B=315 decimal

 

Na tela teríamos: 

AL=315 - 256* = 59 

AL=59 - 6* = 53

AL=53=35h="5"

 

Na tela será apresentado o caractere "5". Onde os números com asterisco e sublinhado são as constantes de conversão.

 

Confesso que levei um bom tempo para "sacar" a lógica de DAA bem como elaborar um exemplo. Para entender mesmo qual a finalidade de DAA observe estes exemplos, mas observe a soma dos números em hexadecimal:

 

Com DAA

 

Sem DAA

Exemplo A

Exemplo B

MOV    AL, 50  ;50=32H="2"

ADD    AL, 22  ;22=16H

DAA

MOV    AL, 50  ;50=32H="2"

ADD    AL, 22  ;22=16H

AL          =   72  =     48H="H" 

AL          =   72   =    48H="H"

 

 

Exemplo C

Exemplo D

MOV    AL, 53 ;53= 35H="5"

ADD    AL, 22  ;22=16H

DAA

MOV   AL, 53  ;53=35H="5"

ADD   AL, 22  ;22=16H

AL         =    81    =  51H="Q"

AL         =    75    =   4BH="K"

 

 

 

 

No exemplo A note que a soma resultou em AL=48h. Ao se fazer  (48h AND  0FH) teremos 08F que é menor que 09h. Portanto, mesmo usando DAA, não é feito nenhum ajuste. A prova disso é visto no exemplo B.

 

Já no exemplo C a soma resultou em AL= 4Bh, antes de ser executada a instrução DAA. Depois que DAA é executada, segue-se a seguinte lógica:

AL=4BH

(AL AND 0F) = 0BH, como 0BH é maior que 09H, fazemos:

AL=AL+6

AL=4BH+6

AL=51H, AF=1

 

Então, depois que DAA é executada, em AL teremos 51H. Note que quando usamos DAA nenhum valor hexadecimal entre 0Ah e 0Fh é permitido. Isso quer dizer o seguinte que a soma de 35h com 32h, números hexadecimais, é feita como se fosse em decimal. Veja:

 

   "+1"

    3     5h

  +3     7h

________

   7       2h

 

35h + 37h, usando-se DAA equivale a 72h, ou seja, 35 + 32 em decimal, quanto é? Não é 72? Por isso podemos montar a seguinte tabela:

 

 

00h

.

.

.

09h

10h

.

.

.

19h

20h

.

.

.

29h

30h

 .

.

.

39h

40h

.

.

.

49h

50h

.

.

.

59h

60h

.

.

.

69h

70h

.

.

.

79h

80h

.

.

.

89h

90h

.

.

.

99h

 

Com um pouco de "imaginação" não poderia ser 0 a 99 em decimal ? :)

Mas isso vou deixar pra você brincar mas tarde. Veja como DAA pode ser usada neste exemplo:

 

 

 

 

 

comment        fim

 

Para compilar isto aqui  basta fazer: ML  nome_do_arq.asm [ENTER] 

Assim que acabar a subtração, multiplicação e divisão e outras coisainhas

prometo colocar aqui o manual do MASM :)

 

Nosso ASM funciona assim: Basta você escolher dois números entre 0 e 9 

e pronto! a soma deles é então mostrada na tela.

 

fim

 

.model small
.stack 1024h

.data
col     db     ?
lin      db     ?
_al     db     ?
_dl     db     ?
flag    db     ?

.code

inicio     proc     near

mov     ax, 0003h    ;limpa tela
int        10h

mov     col, 0
mov     lin, 1    ;chama funcao para posicionar linha e coluna
call       xy     ;imp=procedimento para posicionar cursor  na linha e coluna
                        ;especificados
mov     ah,1    ;imprime tecla
int        21h

mov     _al, al

mov     col, 2
mov     lin,1    ;
call      xy

mov     ah,1
int        21h

add     al,_al
daa

lahf                 ;carregar flags em AH
mov     flag,ah    ;salva os AH na variável flag

mov     _al,al    ;salva o valor da adição em _al
and     al,0Fh  ;zera os 4 bits mais significativos 
add     al,30h  ;soma os 4 bits menos significativos com 30h 

mov     col,2
mov     lin,4    ;ajusta linha e coluna
mov     _dl,al    ;salva AL
call      xy        ;ajusta cursor
call     imp        ;imprime AL

and     flag,10h    ;testa o flag AF, se AF=1 então AL>09h, isso quer
jnz      eh_um       ;dizer que "vai-um" do bit menos significativo para

                             ;mais significativo. JNZ="salte se não for zero".

                            ; Se o resultado anterior for diferente de zero,

                            ; pulamos para o rótulo  "eh_um" se for zero, para 

                            ;o rótulo "eh_zero"

eh_zero:
mov     col,0
mov     lin,4
mov     _dl,"0"
call      xy
call     imp

jmp     fim

eh_um:
mov     col,0
mov     lin,4
mov     _dl,"1"
call      xy
call      imp

fim:
mov     ah,4ch    ;termina todos os processos abertos
int        21h          ;e retorna para o DOS

inicio     endp


xy     proc     near    ;procedimento para posicionar o cursor

mov     ah,2
mov     dl,col
mov     dh,lin
mov     bx,0
int        10h

ret
xy     endp

imp     proc     near    ;procedimento para imprimir caractere

mov     ah,2
mov     dl,_dl
int         21h

ret
imp    endp
          end    inicio

 

Que tal verificar como se comporta os flags depois que se usa DAA e AAA ?

O over2.com simula a soma e subtração usando DAA. Se quiser verificar com AAA, faça o seguinte:

 

1-> Na linha 102, substitua DAA por AAA

2-> Na linha 122, substitua DAA por AAA

3-> Na linha 241, substitua DAA por AAA

4-> Na linha 249, substitua DAA por AAA

5-> Agora basta compilar como um *.com qualquer:

 

MASM over2 ;  ENTER 

 

LINK  / T over2 ; ENTER

 

E pronto! Basta proceder como indicado no ASM Nível 1 E

 

Agora só falta vocês praticarem um pouco!!! :)

 

Mas antes seguem algumas sugestões:

 

Poderíamos simplificar em muito leitura dum programa se pudéssemos dividi-lo em vários procedimentos*. A lógica para se criar um procedimento é esta:

 

nome_do_procedimento    PROC    "distância"

 

RET

nome_do_procedimento    ENDP

 

Onde "distância" pode ser NEAR (perto, no mesmo segmento) ou FAR (longe, em outro segmento).

 

Para chama-lo faça:

 

CALL    nome_do_procedimento    

 

Talvez um dia, queira chamar algum rótulo dentro do seu procedimento. Para tanto, faça o seguinte:

 

LEA        BX, rotulo_dentro_do_procedimento

CALL     BX

 

Lembrando que isso só vale para programas *.com. Se alguém souber como fazer o mesmo para um*.exe manda um-mail, deixa uma mensagem no fórum ou pelo ICQ.

 

Sem mais dicas, vamos as tarefas!

 

* O propósito para um procedimento é executar alguma ação, enquanto o de uma função é devolver um  valor explícito. Mas em Assembly, essa diferença quase não existe, por isso, quando me refiro a procedimento, tanto faz entendê-la como função ou procedimento. 

 

+++++++++++++++++++++++++++++++++++++++++++++++++++++

Nome do projeto: Flag1

Função:  Alterar o estado dos flags para que uma adição seja feita, levando-se em conta o flag AF.

 

A-1> Assim que programa for carregado tem de aparecer :

 

Adição feita levando-se em conta o flag AZ: resultado_da_adição

 

A-2> Onde resultado_da_adição é, como o nome já diz, a adição entre dois operandos de sua escolha.

 

+++++++++++++++++++++++++++++++++++++++++++++++++++++

Nome do projeto: Conte1

 

Função:  Simular o funcionamento dum relógio, usando uma rotina de atraso.

 

A-1> Assim que programa for carregado tem de aparecer :

 

00:00:00

 

A-2> Onde a cada "segundo" o contador dos segundos será incrementado, tal como num relógio comum.

 

 

A-2> A rotina de atraso é essa:

 

relo         PROC    NEAR

MOV      CX, 1000

ATR1:    PUSH  CX

ATR2:    LOOP   ATR2

POP       CX

LOOP     ATR1

RET

relo        ENDP

 

É depois dessa rotina que o contador dos segundos deve ser incrementado.

 

+++++++++++++++++++++++++++++++++++++++++++++++++++++

Nome do projeto: Soma1

 

Função:  Simular o funcionamento duma calculadora. De início a calculadora deve somar operandos de no máximo 2 dígitos, sendo a sua soma máxima de 198 (99+99). Após a soma de dois operandos o resultado é mostrado na tela, e, se ENTER for teclado, uma nova soma será feita, se for ESC o programa termina. A soma deve ser feita dígito por dígito.

 

A-1> Assim que programa for carregado tem de aparecer : 

Entre com os números:

 

 

|

 

A-2> O cursor deve estar na 3ª linha, coluna 0, aguardando os operandos.. Se, qualquer coisa exceto os números de 0 a 9 e o sinal de adição ("+") for digitado, deve ser ignorado, ou seja, não será tido como operando para a soma.

 

A-3> Nesse projeto, poderá usar variáveis para salvar cada dígito, e só depois usá-los para fazer a soma. Por exemplo:

N11=1º dígito do 1º operando

N12=2º dígito do 1º operando

 

N21=1º dígito do 2º operando

N22=2º dígito do 2º operando

 

E fazer as respectivas somas. No Soma2 e Soma3 só poderá usar vetores ou a pilha para isso.

 

+++++++++++++++++++++++++++++++++++++++++++++++++++++

Nome do projeto: Soma2

 

Função:  Simular o funcionamento duma calculadora. De início a calculadora deve somar operandos de no máximo 4 dígitos, sendo a sua soma máxima de 19998 (9999+9999). Após a soma de dois operandos o resultado é mostrado na tela, e, se ENTER for teclado, uma nova soma será feita, se for ESC o programa termina. A soma deve ser feita dígito por dígito.

 

A-1> Assim que programa for carregado tem de aparecer : 

Entre com os números:

 

 

|

 

A-2> O cursor deve estar na 3ª linha, coluna 0, aguardando os operandos.. Se, qualquer coisa exceto os números de 0 a 9 e o sinal de adição ("+") for digitado, deve ser ignorado, ou seja, não será tido como operando para a soma.

 

A-3> Nesse projeto, só poderá usar 3 vetores para salvar os dígitos do 1º e 2º operandos bem como o resultado final.

 

Por exemplo:

 

n1_vetor    [n11, n12, n13, n14]

n2_vetor    [n21, n22, n23, n24]

 

soma[1] = n1_vetor[1] + n2_vetor2[1] 

soma[2] = n1_vetor[2] + n2_vetor2[2] 

soma[3] = n1_vetor[3] + n2_vetor2[3]

.

.

.

A lógica é sua :) faça como achar melhor!!!

 

+++++++++++++++++++++++++++++++++++++++++++++++++++++

Nome do projeto: Soma3

 

Função:  Simular o funcionamento duma calculadora. De início a calculadora deve somar operandos de no máximo 4 dígitos, sendo a sua soma máxima de 19998 (9999+9999). Após a soma de dois operandos o resultado é mostrado na tela, e, se ENTER for teclado, uma nova soma será feita, se for ESC o programa termina. A soma deve ser feita dígito por dígito.

 

A-1> Assim que programa for carregado tem de aparecer :

Entre com os números:

 

 

|

 

A-2> O cursor deve estar na 3ª linha, coluna 0, aguardando os operandos.. Se, qualquer coisa exceto os números de 0 a 9 e o sinal de adição ("+") for digitado, deve ser ignorado, ou seja, não será tido como operando para a soma.

 

A-3> Nesse projeto, só poderá usar a pilha para salvar os dígitos do 1º e 2º operandos e um vetor para salvar o resultado final.

 

MOV    AH, 1

INT      21H

CBW              ;pois retorna caractere em AL e a pilha  só salva em words

                        ;por isso usamos CBW para converter AL em AX

 

PUSH    AX   ; Bem,  a lógica é sua, faça como achar melhor!!!

 

Acho que terá de aprender a manipular a pilha...divirta-se :)

 

Só recomendo declarar e usar funções pois facilita a leitura do código!

 

 

Acho que já chega. Depois das instruções de multiplicação você terá de converter uma "string" em um número "inteiro" e vice-versa, para só então fazer uma soma. Mas isso fica para o ASM Nível 1 G.