Tutorial de Assembler de Adam Hyde 1.0 PARTE 3 Traduzido por Renato Nunes Bastos |
Versão: 1.1
Data: 27-02-1996 / online by Renato 01-11-1998
Contato: blackcat@vale.faroc.com.au
http://www.faroc.com.au/~blackcat
;Renato: Contato
http://www.geocities.com/SiliconValley/Park/3174 (meu site antigo, agora está fora do ar)
http://www.krull.com.br
Um Programa Assembly | O que são Flags? / Comparações
Bem-vindos ao terceiro tutorial da série. No último tutorial eu disse que estaríamos discutindo mais algumas instruções, flags e um verdadeiro programa em Assembly.
Durante este tutorial, você achará os livros “Peter Norton’s Guide to Assembler”, “Peter Norton’s Guide to the VGA Card”, ou qualquer um dos livros “Peter Norton’s Guide to…” muito úteis. Você não pode programar em Assembler sem saber pra que são todas as interrupções e o que são todas as subfunções.
Eu lhe recomendo conseguir uma cópia desses livros assim que possível.
Eu geralmente não escrevo código 100% em Assembly. É muito mais conveniente
usar uma linguagem de alto nível como C ou Pascal, e usar Assembly para
acelerar os bits lentos. Contudo, você pode querer se torturar e escrever uma
aplicação completamente em Assembly, então aqui vai a configuração básica:
DOSSEG | diz à CPU como organizar o segmento |
MODEL | declara o modelo que vamos usar |
STACK | quanta pilha vamos alocar? |
DATA | o que vai no segmento de dados |
CODE | o que vai no segmento de código |
START | o início do seu código |
END START | o fim do seu código |
FATO ENGRAÇADO: Eu sei de alguém que escreveu um clone de Space Invaders (9K), todo em Assembly. Eu tenho o código fonte, se alguém estiver interessado…
Okay, agora vamos dar uma olhada no programa de exemplo em que eu não farei absolutamente nada!
DOSSEG .MODEL SMALL .STACK 200h .DATA .CODE START: MOV AX, 4C00h ; AH = 4Ch, AL = 00h INT 21h END START
Vamos ver em detalhes. Abaixo, cada uma das frases acima está explicada.
- DOSSEG – isto ordena os segmentos na ordem:
Segmentos de Código;
Segmentos de Dados;
Segmentos de Pilha.
Não se preocupe muito com isso por enquanto, apenas inclua, até que você saiba o que está fazendo.
- MODEL – isso permite à CPU determinar como seu programa estáestruturado. Você pode ter os seguintes MODELos:
- TINY – tanto código quanto dados cabem no mesmo segmento de 64K.
- SMALL – código e dados estão em segmentos diferentes, embora cada um tenha menos de 64K.
- MEDIUM – código pode ser maior que 64K, mas os dados têm que ter menos que 64K.
- COMPACT – código ‚ menos de 64K, mas dados podem ter mais que 64K.
- LARGE – código e dados podem ter mais que 64K, embora arrays não possam ser maiores que 64K.
- HUGE – código, dados e arrays podem ter mais de 64K.
- STACK – isso instrui ao PC para arrumar uma pilha tão grande quanto for especificado.
- DATA – permite que você crie um segmento de dados.P or exemplo:
MySegment SEGMENT PARA PUBLIC ‘DATA’
; Declare alguns bytes, words, etc.
MySegment ENDS
Isso é similar a CONSTANTES in Pascal.
- CODE – permite a você criar um segmento de código. Ex.:
MyCodeSegment SEGMENT PARA PUBLIC ‘CODE’
; Declare alguma coisa
MyCodeSegment ENDS
- START – Apenas um label para dizer ao compilador onde a parte principal do seu programa começa.
-
MOV AX, 4C00h ; AH = 4Ch, AL = 00h
Isso move 4Ch para ah, que coincidentemente nos traz de volta ao DOS.
Quando a interrupção 21h é chamada e AH = 4Ch, de volta ao DOS lá vamos nós.
- INT 21h
- END START – Você não tem imaginação?
OK, espero que você tenha entendido tudo isso, porque agora nós vamos mesmo fazer algo.
Ficou excitado? 🙂
Neste exemplo nós vamos usar a interrupção 21h, (a interrupção do DOS), para imprimir uma string. Para ser mais preciso, vamos usar a subfunção 9h, e ela se parece com isso:
- INTERRUPÇÃO 21h
- SUBFUNÇÃO 9h
Essa função requer:
-
AH = 9h
-
DS:DX = ponteiro FAR para a string a ser impressa. A string deve ser terminada com um sinal $.
Assim, aqui está o exemplo:
DOSSEG .MODEL SMALL .STACK 200h .DATA OurString DB "Isto é uma string de caracteres. " DB "Você não tem imaginação? Coloque algo interessante aqui!$" .CODE START: MOV AX, SEG OurString ; Move o segmento onde OurString está MOV DS, AX ; para AX, e agora para DS MOV DX, OFFSET OurString ; Offset de OurString -> DX MOV AH, 9h ; Subfunção de imprimir strings INT 21h ; Gera a interrupção 21h MOV AX, 4C00h ; Subfunção de saída para o DOS INT 21h ; Gera a interrupção 21h END START
Se você assemblar isso com TASM – TASM SEJALADOQUEVOCECHAMOUELE.ASM então linkar com TLINK – TLINK SEJALADOQUEVOCECHAMOUELE.OBJ você vai obter um arquivo EXE de cerca de 652 bytes. Você pode usar estes programas no DEBUG com algumas modificações, mas eu vou deixar isso contigo. Para trabalhar com Assembly puro você _precisa_ de TASM e TLINK, embora eu ache que MASM <aahh!> faria o mesmo trabalho muito bem.
Agora vamos ao código, com um pouco mais de detalhes:
MOV AX, SEG OurString ; Move o segment onde OurString está MOV DS, AX ; para AX, e agora para DS MOV DX, OFFSET OurString ; Move o offset onde OurString está localizado MOV AH, 9h ; Subfunção de escrita de strings INT 21h ; Gera a interrupção 21h
Você vai notar que tivemos que usar AX para pôr o endereço do segmento de OurString em DS. Você vai descobrir que não dá pra referenciar um registrador de segmento diretamente em Assembly. Na procedure PutPixel do último tutorial, eu movi o endereço da VGA para AX, e depois para ES. A instrução SEG também foi introduzida. SEG retorna o segmento onde a string OurString está localizada, e OFFSET retorna, adivinha o quê? O offset do início do segmento para onde a string termina!
Note também que nós usamos DB. DB não é nada de especial, e significa Declare Byte, que é o que ela faz. DW, Declare Word e DD, Declare Double Word também existem.
Você poderia ter também colocado OurString no segmento de código, a vantagem é que CS estaria apontando para o mesmo segmento que OurString, de modo que você não tem que se preocupar em procurar o segmento em que OurString está.
O programa acima no segmento de código seria mais ou menos assim:
DOSSEG .MODEL SMALL .STACK 200h .CODE OurString DB "Abaixo o segmento de dados!$" START: MOV AX, CS MOV DS, AX MOV DX, OFFSET OurString MOV AH, 9 INT 21h MOV AX, 4C00h INT 21h END START
Simples, não?
Nós não vamos ver muitos programas só em Assembly de novo, mas a maioria das técnicas que usaremos podem ser implementadas em programas só em Assembler.
Esta parte é para meu companheiro Clive que tem me perguntado sobre flags, então lá vamos nós Clive, com FLAGS.
Eu não me lembro se já introduzimos a instrução CMP (COMPARE) ou não, mas CMP compara dois números e reflete a comparação nos FLAGS. Para usá-la você faria algo desse tipo:
- CMP AX, BX
então seguir com uma instrução como essas abaixo: COMPARAÇÕES SEM SINAL:
Comparação
|
Descrição
|
---|---|
JA | pula (jump) se AX foi MAIOR que BX |
JAE | pula se AX foi MAIOR ou IGUAL a BX |
JB | pula se AX foi MENOR que BX |
JBE | pula se AX foi MENOR ou IGUAL a BX |
JNA | pula se AX foi NÃO MAIOR que BX |
JNAE | pula se AX foi NÃO MAIOR ou IGUAL a BX |
JNB | pula se AX foi NÃO MENOR que BX |
JNBE | pula se AX foi NÃO MENOR ou IGUAL a BX |
JZ | pula se o flag de ZERO está setado – o mesmo que JE |
JE | pula se AX for IGUAL a BX |
JNZ | pula se o flag de ZERO NÃO está setado – o mesmo que JNE |
JNE | pula se AX NÃO for IGUAL a BX |
COMPARAÇÕES COM SINAL:
Comparação
|
Descrição
|
---|---|
JG | pula (jump) se AX foi MAIOR que BX |
JGE | pula se AX foi MAIOR ou IGUAL a BX; |
JL | pula se AX foi MENOR que BX |
JLE | pula se AX foi MENOR ou IGUAL a BX |
JNG | pula se AX foi NÃO MAIOR que BX |
JNGE | pula se AX foi NÃO MAIOR ou IGUAL a BX |
JNL | pula se AX foi NÃO MENOR que BX |
JNLE | pula se AX foi NÃO MENOR ou IGUAL a BX |
JZ | pula se o flag de ZERO está setado – o mesmo que JE |
JE | pula se AX for IGUAL a BX |
JNZ | pula se o flag de ZERO NÃO está setado – o mesmo que JNE |
JNE | pula se AX NÃO for IGUAL a BX |
NÃO TÃO COMUNS:
Comparação
|
Descrição
|
---|---|
JC | pula se o flag de CARRY está setado |
JNC | pula se o flag de CARRY NÃO está setado |
JO | pula se o flag de OVERFLOW está setado |
JNO | pula se o flag de OVERFLOW NÃO está setado |
JP | pula se o flag de PARIDADE está setado |
JNP | pula se o flag de PARIDADE NÃO está setado |
JPE | pula se a PARIDADE for PAR – o mesmo que JP |
JPO | pula se a PARIDADE for ÍMPAR – o mesmo que JNP |
JS | pula se o flag de SINAL NÃO está setado |
JNS | pula se o flag de SINAL está setado |
Ufa! Meus olhos quase secaram depois de olhar pra essa tela por tanto tempo!
De qualquer modo, aqui está com o que eles se parecem:
Flag | SF | ZF | — | AF | — | PF | — | CF |
Bit | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
Legenda:
SF – Flag de Sinal;
ZF – Flag de Zero;
AF – Flag Auxiliar;
PF – Flag de Paridade;
CF – Flag de Carry (vai um).
Obs.: HÁ MUITO MAIS FLAGS PARA APRENDER. Eles serão vistos num Tutorial mais à frente.
COISAS PARA FAZER:
- Volte ao frame da configuração básica de Assembler e memorize-o.
- Tenter escrever um programa simples que mostre alguns comentários _criativos_.
- Aprenda as instruções JUMP menos criptográficos de cor.
Okay, no último tutorial eu lhe dei algumas procedures, e pedi para você comentá-las. Eu não queria uma explicação detalhada do que eles faziam – não se espera que você saiba isso ainda – apenas um sumário do que cada comando faz.
Ex.:
MOV AX, 0003h ; AX agora ‚ igual a 03h; ADD AX, 0004h ; AX agora ‚ igual a 07h; Então, aqui vai o conjunto completo das procedures com comentários: { Esta procedure limpa a tela em modo texto } Procedure ClearScreen(A : Byte; Ch : Char); Assembler; Asm { ClearScreen } mov ax, 0B800h { Move o endereço de vídeo para AX } mov es, ax { Aponta ES para o segmento de vídeo } xor di, di { Zera DI } mov cx, 2000 { Move 2000 (80x25) para CX } mov ah, A { Move o atributo para AH } mov al, &Ch { Move o caracter a usar para AL } rep stosw { Faz isso } End; { ClearScreen } Explicação: Nós zeramos DI, logo é igual a 0 - o canto esquerdo da tela. Isto é de onde vamos começar a encher a tela. Movemos 2000 para CX porque vamos colocar 2000 caracteres na tela. { Esta procedure move o cursor para a posição X, Y } Procedure CursorXY(X, Y : Word); Assembler; Asm { CursorXY } mov ax, Y { Move o valor Y para AX } mov dh, al { Y vai para DH } dec dh { rotina baseada em ajustar para zero } mov ax, X { Move o valor de X para AX } mov dl, al { X vai para DL } dec dl { rotina baseada em ajustar para zero } mov ah, 2 { Chama a função correspondente } xor bh, bh { Zera BH } int 10h { faz isso (põe o cursor na posição) } End; { CursorXY }
Explicação:
A ‘rotina baseada em ajustar para zero’ é realizada porque a BIOS refere-se à posição (1, 1) como (0, 0), e igualmente (80, 25) como (79, 24).
Procedure PutPixel(X, Y : Integer; C : Byte; Adr : Word); Assembler; Asm { PutPixel } mov ax, [Adr] { Move o endereço do VGA em AX } mov es, ax { Joga AX em ES } mov bx, [X] { Move o valor de X para BX } mov dx, [Y] { Move o valor de Y para DX } xchg dh, dl { Daqui pra frente calcula o } mov al, [C] { offset do pixel a ser plotado } mov di, dx { e põe este valor em DI. Vamos } shr di, 2 { ver isso mais tarde - próximo tutorial } add di, dx { quando falarmos sobre shifts } add di, bx { versus muls } stosb { Guarda o byte em ES:DI } End; { PutPixel }
NOTA: Eu estaria muito interessado em achar uma procedure PutPixel mais rápida que essa. Eu já vi uma inline que faz isso em metade do tempo, mas mesmo assim, essa é muito quente.
{ Esta procedure ‚ uma função de delay independente de CPU } Procedure Delay(ms : Word); Assembler; Asm { Delay } mov ax, 1000 { Move o número de ms em um segundo para AX } mul ms { Faz AX = número de ms a esperar } mov cx, dx { Prepara para o delay - põe número de ms } mov dx, ax { onde necessário } mov ah, 86h { Cria o delay } int 15h End; { Delay }
Quase todo o fluido saiu do meus olhos agora – é quase meia-noite – então
eu acho melhor parar. Desculpe se os comentários são um pouco curtos, mas eu
preciso dormir!
No próximo tutorial vamos ver:
- Shifts – o que são eles?
- Alguns exemplos de CMP/JMP.
- Como a memória VGA é arrumada, e como acessá-la.
- hum, algum outro grande tópico.
Na próxima semana eu vou fazer um esforço para te mostrar com acessar a memória rapidamente, isto é, o VGA, e lhe dar alguns exemplos.
Se você deseja ver um tópico discutido num tutorial no futuro, escreva-me, e eu vou ver o que eu posso fazer.
Não perca!!! Baixe o tutorial da próxima semana na minha homepage:
Vejo vocês na próxima semana!
– Adam.
– Renato Nunes Bastos
Excelente trabalho, e parabéns por também buscar ajudar aqueles que querem aprender 🙂