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  :  rnbastos@ig.com.br 
                 http://www.geocities.com/SiliconValley/Park/3174


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.


Um Programa Assembly

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 quê vai no segmento de dados
CODE o quê 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.

                  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.

  1. TINY          - tanto código quanto dados se encaixam no mesmo segmento de 64K.
  2. SMALL       - código e dados estão em segmentos diferentes, embora cada um tenha menos de 64K.
  3. MEDIUM    - código pode ser maior que 64K, mas os dados têm que ter menos que 64K.
  4. COMPACT - código ‚ menos de 64K, mas dados podem ter mais que 64K.
  5. LARGE       - código e dados podem ter mais que 64K, embora arrays não possam ser maiores que 64K.
  6. HUGE          - código, dados e arrays podem ter mais de 64K.

                  MySegment SEGMENT PARA PUBLIC 'DATA'

                      ; Declare alguns bytes, words, etc.

                  MySegment ENDS

                  Isso é similar a CONSTANTES in Pascal.

                  MyCodeSegment SEGMENT PARA PUBLIC 'CODE'

                      ; Declare algo

                  MyCodeSegment ENDS

      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.

Okay, Espero que você tenha entendido tudo isso, porque agora nós vamos mesmo fazer algo.  Ficou excitado?  :)

    Neste examplo nós vamos usar a interrupção 21h, (a interrupção do DOS), para imprimir uma string. Para ser preciso, vamos usar a subfunção 9h, e ela se parece com isso:

   Requer:

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 conseguir 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 Assembler 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 detalhado:

    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 Assembler. Na procedure PutPixel do último tutorial, eu movi o endereço da VGA para AX, e então para ES. A instrução SEG é também 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 é tudo o que ela faz.  DW, Declare Word e DD, Declare Double Word também existem.

Você poderia ter também colocado OurString segmento de código, a vantagem é que CS estaria apontando para o mesmo segmento que OurSting, 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.


Então, o que são flags?

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:

então seguir com uma instrução como essas abaixo:

COMPARAÇÕES SEM SINAL:

COMPARAÇÕES COM SINAL:

NÃO TÃO COMUNS:

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).

Nota: HÁ MUITO MAIS FLAGS PARA APRENDER. Eles serão vistos num Tutorial mais à frente.


COISAS PARA FAZER:

1) Volte ao frame da configuração básica de Assembler e memorize-o.
2) Tenter escrever um programa simples que mostre alguns comentários _criativos_.
3) 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:

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