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.