

          ͻ
                    Tutorial de Assembler de Adam Hyde 1.0        Ŀ
                                                                   
                                   PARTE VIII                      
                      Traduzido por Renato Nunes Bastos            
          ͼ 
            


Verso   :  1.2
Data     :  28-06-1996
Contato  :  blackcat@vale.faroc.com.au
            http://www.faroc.com.au/~blackcat
;Renato  :  bastos@lci.ufrj.br
            krull@geocities.com
            http://www.geocities.com/SiliconValley/Park/3174

  

Bem, bem-vindo de volta programadores de Assembler.  Este tutorial est realmente 
atrasado, e teria chegado muito mais tarde se no fosse por Bjorn Svensson, e 
muitos outros como ele, que graas  sua determinao em adquirir Tutorial 8, me 
persuadiu a escrever esta coisa.  claro, isto significa que eu provavelmente 
fracassei em todos meus exames das ltimas duas semanas, mas a vida  assim. :)


Ok, esta semana ns vamos realmente aprender algo.  Vamos dar uma olhada 
mais de perto em como podemos declarar variveis, e aprofundar no mundo de 
estruturas.  Voc aprender a criar arrays em Assembler, e este conceito  
reforado com o programa demonstrativo que eu inclu - uma rotina de fogo! 


  

         Ŀ
                                                                   
                        ESTRUTURAS DE DADOS EM ASSEMBLER           
                                                                   
         


Bem, at agora voc deveria saber que voc pode usar o DB, (Declare Byte) e 
DW, (Declare Word) para criar variveis. Porm, at agora ns os temos usado como 
voc usaria a declarao de Const em Pascal. Quer dizer, temos usado isto para
dar a um byte ou a uma word um valor.

Ex.: 

	MyByte DB 10  --  que  o mesmo que  --  Const MyByte : Byte = 10;


Contudo, poderamos dizer: 

   MyByte DB ? 

...e ento dizer depois:

   MOV MyByte, 10


De fato DB realmente  muito poderoso. H vrios tutoriais atrs, quando voc 
estava aprendendo a escrever strings na tela, voc viu algo desse tipo: 

   MyString DB 10, 13 "This is a string$"


Agora, o mais curioso de vocs provavelmente teria dito a si prrpio: "Pera!... 
aquele cara do tutorial disse que DB declara um BYTE.  Como  que o DB
pode declarar uma string, ento "?  Bem, DB tem a habilidade de reservar espao 
para valores de vrios bytes - de 1 a tantos bytes quanto voc precisa. 

Voc tambm pode ter desejado saber o que os nmeros 10 e 13 antes do texto 
representavam. Bem, d uma olhada na sua tabela ASCII e veja o que so o 10 e 
o 13.  Voc notar que 10  o Line Feed e o 13  o Carriage Return. 
Basicamente,  o mesmo que dizer: 

   MyString := #10 + #13 + 'This is a string';

em Pascal. 

  


Ok, ento voc viu como criar variveis corretamente. Mas, e constantes? 
Bem, em Assembler, constantes so conhecidas como Equates. Equates fazem a 
cofificao em Assembler muito mais fcil, e pode simplificar muito as coisas.
Por exemplo, se eu tivesse usado o seguinte em tutoriais anteriores: 

   LF   EQU 10
   CR   EQU 13

   DB   LF, CR "Isso  uma string$"


...as pessoas teriam entendido direito aquela coisa de 10 e 13. Mas, para fazer as 
coisas um pouco mais complicadas, h ainda um outro modo que voc pode usar para dar
valores a identificadores. Voc pode fazer como voc faria em BASIC: 

   Population  = 4Ch
   Magnitude   = 0


Basicamente, voc pode ter em mente os seguintes pontos: 

    Uma vez que voc tenha usado EQU para dar um valor a um identificador, voc no pode 
mudar isto.

    EQU pode ser usado para definir quase qualquer tipo - inclusive strings.  
Contudo, voc no pode fazer isto quando se usa um ' = '.  Um ' = ' s pode 
definir valores numricos. 

    Voc pode usar EQU quase em qualquer lugar de seu programa. 

    Valores definidos com ' = ' podem ser mudados. 

  


E agora, vamos a um dos pontos mais macetosos de codificao em Assembler - 
estruturas. Estruturas no so variveis, so um TIPO - basicamente um esquema
de uma varivel. 

Como um exemplo, se voc tivesse o seguinte em Pascal: 

   Type
      Date      = Record;
         Day    : Byte;
         Month  : Byte;
         Year   : Word;
      End;    { Record }

Voc poderia representar isto em Assembler como segue: 

   Date         STRUC
      Day       DB ?
      Month     DB ?
      Year      DW ?
   Date         ENDS


Porm, um das vantagens de Assembler  que voc pode inicializar todos ou alguns 
dos campos da estrutura antes mesmo de voc se referir  estrutura em seu 
segmento de cdigo. 

Aquela estrutura acima poderia ser escrita facilmente como: 
	
   Date         STRUC
      Day       DB ?
      Month     DB 6
      Year      DW 1996
   Date         ENDS


Alguns pontos importantes para se lembrar so os seguintes: 

    Voc pode declarar uma estrutura em qualquer lugar em seu cdigo, embora 
que, para um bom design, voc deva coloc-los no segmento de dados, a menos que eles 
s sejam usados por uma subrotina. 

    Definir uma estrutura no reserva qualquer byte de memria para a mesma. Isso 
s acontece quando voc declara uma varivel dessa estrutura - a a memria  alocada. 

  

          Ŀ
                                                                    
               REFERENCIANDO ESTRUTURAS DE DADOS EM ASSEMBLER       
                                                                    
          


Bem, voc viu como definir estruturas, mas como voc se refere de verdade a elas 
em seu cdigo? 

Tudo o que voc tem a fazer,  colocar em algum lugar algumas linhas como as seguintes 
em seu programa - de preferncia no segmento de dados. 

   Date         STRUC
      Day       DB 19
      Month     DB 6
      Year      DW 1996
   Date         ENDS

   Date_I_Passed_Physics   Date <>   ; Espero!


Neste momento, Date_I_Passed_Physics tem todos os seus trs campos preenchidos. 
Dia  setado para 19, Ms para 6 e Ano para 1996. Agora, o que so esses 
parnteses,"<>", fazendo depois de data? - voc pergunta.

Os parnteses nos apresentam um outro modo de alterar os contedos 
dos campos da varivel.  Se eu tivesse escrito isto: 

   Date_I_Passed_Physics   Date <10,10,1900>


...ento os campos teriam sido mudados para os valores nos parnteses. 
Alternativamente, teria sido possvel fazer isto: 

   Date_I_Passed_Physics   Date <,10,>   ;


E s agora o campo de Ms foi mudado.  Note que neste exemplo, a segunda 
vrgula no era necessria, pois ns no mudamos outros campos posteriores.
 sua escolha, (e do compilador!), se deixar a segunda vrgula ou no. 


Agora tudo isso t indo muito bem, mas como voc se estes valores em seu cdigo? 
Simplesmente basta dizer: 

   MOV   AX, [Date_I_Passed_Physics.Month]    ; ou algo como

   MOV   [Date_I_Passed_Physics.Day], 5       ; ou at mesmo

   CMP   [Date_I_Passed_Physics.Year], 1996


Simples, n? 

  

          Ŀ
                                                                    
                        CRIANDO ARRAYS EM ASSEMBLER                 
                                                                    
          


Certo, arrays so bem fceis de se implementar. Por exemplo, 
digamos que voc tivesse a seguintes estrutura de array em Pascal: 

   Var 
      MyArray: Array[0 ..19] of Word; 


Para criar um array semelhante em Assembler, voc tem que usar o operador 
DUP.  DUP, ou DUPlique Varivel, tem a seguinte sintaxe: 

    <rtulo>    <diretiva> <contador>  DUP  (expresso)

   Onde (expresso)  um valor opcional para inicializar o array. 


Basicamente, aquele array no Pascal se pareceria com isso: 

   MyArray DW 20 DUP (?) 


Ou, se voc quisesse inicializar cada valor para zero, ento voc poderia 
dizer isto: 

   MyArray DW 20 DUP (0) 


E, como outro exemplo de como o Assembler  flexvel, voc poderia dizer 
algo desse tipo: 

   MyArray DB 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,,, 


...para criar um array de 10 bytes, com todos os dez elementos inicializados em 1, 
2, 3... 

  

          Ŀ
                                                                    
                        INDEXANDO ARRAYS EM ASSEMBLER               
                                                                    
          


Bem, agora que voc j viu como criar arrays, eu suponho que voc queira saber como 
referenciar elementos individualmente. Bem, digamos que voc tivesse o seguinte array:

   OutroArray DB 50 DUP (?) 


Se voc quisesse mover o elemento 24 para, digamos, BL, ento voc poderia fazer isto: 

   MOV BL, [OutroArray + 23]; Ou, seria possvel dizer: 

   MOV AX, 23, 
   MOV BL, [OutroArray + AX] 


NOTA:  No esquea que todos os arrays comeam no elemento ZERO. Linguagens de 
alto-nvel como C e Pascal fazem voc esquecer isto devido ao modo que eles 
deixam voc referenciar arrays. 

  


Agora, isso foi fcil, mas, e se OutroArray fosse de 50 WORDS, no BYTES? 

   OutroArray DW 50 DUP (?)   ; como esse. 


Bem, para acessar o elemento 24, voc teria que multiplicar o valor de ndice por dois, 
e ento somar isso a OutroArray para conseguir o elemento desejado. 

   MOV AX, 23			; Acesso elemento 24 
   SHL AX, 1			; Multiplique AX por dois 
   MOV BX, [OutroArray + AX]	; Adquira elemento 24 em BX 


No  to difcil assim, n? Porm, este mtodo fica um pouco macetoso quando 
voc no tem clculos fceis para fazer quando o ndice no  uma potncia de dois. 

Digamos que voc tivesse um array que tem um tamanho de elemento de 5 bytes. 
Se ns quisssemos conferir o stimo elemento, ns teramos que fazer algo 
assim: 

   MOV AX, 6				; Pega o stimo elemento 
   MOV BX, 5				; Cada elemento tem cinco bytes
   MUL BX				; AX = 6 x 5
   MOV DX, [YetAnotherArray + AX]	; Coloca o elemento 7 em DX 


Porm, como eu disse antes, MUL no  um modo muito eficiente de codificao, 
assim, substituir o MUL por um SHL 2 e um ADD seria a ordem do dia.

  


Antes de continuarmos com mais alguma coisa, eu suponho que seja hora de  
falar sobre nmeros de ponto flutuante. Agora, nmeros de ponto flutuantes 
podem ser desajeitados para se manipular em Assembler, assim v se no sai 
escrevendo aquele programa de planilha eletrnica que voc sempre quis, em 
cdigo de mquina! Porm, quando estiver trabalhando com mapeamento de textura,
crculos e outras funes mais complicadas,  inevitvel que voc precise de 
algo para declarar nmeros de ponto flutuante. 

Digamos que quisssemos armazenar Pi. Para declarar Pi, ns precisamos usar a
DT diretiva. Voc poderia declarar Pi assim: 

Pi DT 3.14 


DT na verdade reserva dez bytes de memria, assim seria possvel declarar Pi com
um nmero maior de casas decimais. 

Eu no vou entrar nas particularidades de nmeros de ponto flutuante neste 
tutorial. Quando ns precisarmos deles mais tarde, eu falo sobre isso. 

  


Certo, no ltimo tutorial disse eu que eu daria algum tipo de resumo do que ns 
cobrimos durante os ltimos quatro meses.  (Ei - isso  como se fosse um tutorial 
a cada duas semanas, ento talvez eles no tenham sado to irregularmente, afinal 
de contas!) 

De qualquer maneira, eu vou falar sobre a parte de pegar e setar bits individuais 
num registrador, porque este  um tpico importante que eu deveria ter coberto h 
muito tempo atrs. 

  


          Ŀ
                                                                    
                              OPERADORES LGICOS                    
                                                                    
          


Certo, de volta ao Tutorial Cinco, eu dei as trs tabelas verdade para E, OU 
e XOR. 

(A propsito, em uma edio de Tutorial Cinco, eu errei a tabela para XOR, 
amavelmente apontado por Keith Weatherby, assim se voc no tem a verso 
mais atual, (V 1.3), ento pegue agora.  Por favor, embora eu tente o meu melhor 
para excluir qualquer erro dos Tutoriais, alguns ficam com erros, assim se voc achar 
algum, por favor me avise. 

Mas tenha certeza de que voc tem as edies mais recentes dos tutoriais antes de fazer 
isto!) 

Certo, chega de meus erros. Essas tabelas se pareciam com estas: 


                      AND             OR             XOR

                  0 AND 0 = 0     0 OR 0 = 0     0 XOR 0 = 0
                  0 AND 1 = 0     0 OR 1 = 1     0 XOR 1 = 1
                  1 AND 0 = 0     1 OR 0 = 1     1 XOR 0 = 1
                  1 AND 1 = 1     1 OR 1 = 1     1 XOR 1 = 0


Isto est tudo muito bem, mas pra qu vamos usar isso? Bem, em primeiro 
lugar, vamos dar uma olhada no que o AND pode fazer. Ns podemos usar o AND 
para mascarar bits em um registrador ou varivel, e assim setar e resetar bits 
individuais.

Como um exemplo, usaremos o AND para testar um valor de um nico bit. Olhe os 
exemplos seguintes, e veja como voc pode usar AND para seus prprios fins. Um 
uso bom para AND seria conferir se um caracter lido do teclado  uma maiscula
ou no.  (Voc pode fazer isto, porque a diferena entre uma maiscula e sua minscula 
 de um bit. 


   Ex:  'A' =  65   = 01000001
        'a' =  97   = 01100001

        'S' =  83   = 01010011
        's' =  115  = 01110011)

Assim, da mesma forma que voc pode azer um AND de nmeros binrios, voc 
poderia usar uma aproximao semelhante para escrever uma rotina que confere se 
um caracter  maisculo ou minsculo. 


   Ex:         0101 0011                             0111 0011
           AND 0010 0000                         AND 0010 0000

             = 0000 0000                           = 0010 0000

      ^^^ Essa  maiscula ^^^               ^^^ Essa  minscula ^^^


Agora, e o OR? O OR  geralmente usado depois de um AND, mas no tem 
que ser.  Voc pode usar OR para mudar bits individuais em um registrador ou 
varivel sem mudar quaisquer um dos outros bits. Voc poderia usar OR para escrever 
uma rotina para mudar um caracter para maisculo se j no for, ou talvez para minscula 
se fosse maiscula. 


   Ex:                             0101 0011
                                OR 0010 0000

                                =  0111 0011

            ^^^ S maisculo agora foi mudado para s minsculo ^^^


A combinao de AND/OR  um dos truques mais frequentemente usados no mundo do 
Assember, assim tenha certeza de que voc entendeu bem o conceito.  Voc me ver 
freqentemente usando-os, tirando proveito da velocidade das instrues. 

Finalmente, e o XOR?  Bem, o OU exclusivo pode ser s vezes muito til. 
XOR pode ser de til para alternar bits individuaisentre 0 e 1 sem 
ter que saber qual o contedo que cada bit tinha anteriormente. Lembre-se, 
como com OU, uma mscara de zero permite ao bit original continuar com seu valor. 

   Ex:                            1010 0010
                              XOR 1110 1011

                                = 0100 1001


Faa alguma tentativa para aprender estes operadores binrios, e o que eles 
fazem.  Eles so uma ferramenta inestimvel quando se est trabalhando com 
nmeros binrios. 

OBS.:  Para simplicidade, o Turbo Assembler lhe permite usar nmeros binrios 
em seu cdigo.  Por exemplo, seria possvel dizer, AND AX, 0001000b em vez de AND 
AX, 8h para testar o bit 3 de AX. Isto pode facilitar as coisas para voc quando 
codificar. 

  

          Ŀ
                                                                    
                              O PROGRAM DEMONSTRATIVO               
                                                                    
          


Certo, chega da parte chata - vamos ao programa demonstrativo que eu inclu!
Eu pensei que j era sem tempo escrever outra demonstrao - 100% Assembler 
desta vez, e vamos a uma rotina de fogo. Rotinas de fogo podem parecer bem 
efetivas, e so surpreendentemente fceis de se fazer, assim, pensei, 
por que no... 

  


Agora, os princpios de uma rotina de fogo so bastante simples.  Voc basicamente 
faz o seguinte: 

    Crie um buffer com o qual voc vai trabalhar  

     Este buffer pode ser quase de qualquer tamanho, entretanto quanto menor 
voc o fizer, o mais rpido seu programa ser, e quanto maior voc o fizer, o mais 
bem definido o fogo ser. Voc precisa acertar um equilbrio entre claridade e 
velocidade. 

     Minha rotina est um pouco lenta, e isto  devido em parte  claridade 
do fogo.  Eu escolhi 320 x 104 como tamanho do meu buffer, assim eu fiz um 
compromisso. A resoluo horizontal  boa - 1 pixel por elemento de array, 
mas a resoluo vertical  um pouco baixa - 2 pixels por elemento de array. 

     Contudo, eu j vi rotinas onde um buffer de 80 x 50  usado, significando 
que h 4 pixels por elemento para o eixo horizontal e vertical.   rpido, mas 
com baixssima definio. 


    Faa uma palette agradvel 

     Seria idia boa para ter cor 0 como preto, (0, 0, 0) e a cor 255 como 
branco - (63, 63, 63).  Tudo entre isso deveria ser uma mistura de 
amarelo-avermelhado flamejante. Eu suponho voc poderia ter chamas verdes se voc 
quisesse, mas ns vamos usar as chamas que ns conhecemos agora.  :) 


Agora o loop principal comea. No loop voc deve: 

    Criar uma linha de fundo ramdmica, ou duas linhas de fundo 

     Basicamente, voc tem um loop como: 

     For X := 1 To Xmax Do
      Begin
         Temp := Random(256);
         Buffer[X, Ymax - 1] := Temp;
         Buffer[X, Ymax]     := Temp;
      End;

      Codifique isso na linguagem de sua escolha, e voc est no negcio. 


    Suavize o array:

     Agora este  o nico pedao com macete. O que voc tem que fazer,  como 
segue: 

       * Comece da segunda linha pra baixo do buffer. 
       * Mover para baixo, e para cada pixel: 

         * Some os valores dos quatro pixels que cercam o pixel. 
         * Divida o total por quatro conseguir uma mdia. 
         * Tire um da mdia. 
         * Ponha a mdia - 1 no array DIRETAMENTE ACIMA onde o pixel velho estava. 
(Voc pode alterar isto, e digamos, pr acima e  direita, e ento 
parecer que a chama est sendo soprada pelo vento.) 

       * Faa isso at voc chegar  ltima linha. 


    Copie o array para a tela 

     Se seu array  de 320 x 200, ento voc pode copiar elemento-para-pixel.  Se 
no , ento coisas so mais difceis. O que eu tive que fazer era copiar uma linha 
do array para a tela, abaixar uma linha da tela, copiar a mesma linha do array 
para a tela, e ento entrar numa linha diferente no array e na tela. 

     Deste modo, eu espalhei o fogo um pouco. 

     Voc vai, , querer saber exatamente por que meu array  de 320 x 104 e 
no de 320 x 100.  Bem, a razo para isto  bastante simples.  Se eu tivesse 
usado 320 x 100 como minhas dimenses de array, e ento copiasse isso para a 
tela, as ltimas quatro linhas teriam parecido bem estranhas.  Elas 
no teriam sido suavizados corretamente, e o resultado final no estaria de
todo flamejante.  Assim, eu apenas copiei at a linha 100 para a tela, e deixei o 
resto pra l. 

     Como uma experincia, tente mudar a terceira linha abaixo no 
procedimento de DrawScreen para   MOV BX, BufferY   e mudar as dimenses para
320x100 e veja o que acontece. 

     MOV   SI, OFFSET Buffer          ; Aponta SI para o incio do buffer
     XOR   DI, DI                     ; Comea a desenhar em 0, 0
     MOV   BX, BufferY - 4            ; Perde as 4 ltimas linhas do
                                      ; buffer. Estas linhas no vo se parecer
                                      ; com fogo de jeito nehum.


    Volta para o incio. 

  


Bem, no importa o quo bem eu expliquei isso tudo,  muito difcil de ver o 
que est acontecendo sem olhar o cdigo. Ento agora ns vamos dar uma olhada no
programa, seguindo o que est acontecendo. 

Bem, em primeiro lugar, voc tem o header. 


   .MODEL SMALL   ; Segmento de dados < 64K, segmento de cdigo < 64K
   .STACK 200H    ; Arruma 512 bytes de espao para a pilha
   .386


Aqui, eu disse que o programa ter um segmento de cdigo e de dados 
total de menos que 128K. Eu vou dar para o programa uma pilha de 512
bytes, e permitir instrues do 386. 


  .DATA 

CR 	EQU 13 
LF 	EQU 10 


O segmento de dados comea, e eu dou para CR e para LF os valores de "carriage return" e 
"line feed" (retorno de carro e alimentao de linha, i.e, volta pro incio e desce uma 
linha).


BufferX   EQU 320                       ; Largura do buffer de tela
BufferY   EQU 104                       ; Altura do buffer de tela

AllDone   DB CR, LF, "That was:"
          DB CR, LF
          DB CR, LF, "         FFFFFFFFF    IIIIIII     RRRRRRRRR    ..."
          DB CR, LF, "          FFF           III        RRR   RRR   ..."
          DB CR, LF, "          FFF           III        RRR   RRR   ..."
          DB CR, LF, "          FFF           III        RRRRRRRR    ..."
          DB CR, LF, "          FFFFFFF       III        RRRRRRRR    ..."
          DB CR, LF, "          FFF           III        RRR  RRR    ..."
          DB CR, LF, "          FFF           III        RRR   RRR   ..."
          DB CR, LF, "          FFF           III        RRR    RRR  ..."
          DB CR, LF, "         FFFFF        IIIIIII     RRRR    RRRR ..."
          DB CR, LF
          DB CR, LF
          DB CR, LF, "   The demo program from Assembler Tutorial 8. ..."
          DB CR, LF, "   author, Adam Hyde, at: ", CR, LF
          DB CR, LF, "      blackcat@faroc.com.au"
          DB CR, LF, "      http://www.faroc.com.au/~blackcat", CR, LF, "$"

Buffer    DB BufferX * BufferY DUP (?) ; O buffer de tela

Seed      DW 3749h                     ; O valor de seed, e metado do meu nmero de
                                       ; telefone - no em hexa. :)

INCLUDE PALETTE.DAT                    ; A palette, gerada com
                                       ; Autodesk Animator, e um programa simples em 
                                       ; Pascal.



Agora, no fim, eu declaro o array e declaro um VALOR DE SEED (semente) para o 
procedimento Random que segue. A seed  s um nmero que  necessrio para 
comear o procedimento Random, e pode ser qualquer coisa que voc quiser. 

Eu tambm economizei algum espao e pus os dados para a palette em um arquivo 
externo que  includo no cdigo assembly. D uma olhada no arquivo.
Usar INCLUDE pode economizar muito espao e confuso. 


Eu pulei alguns procedimentos que so bastante auto-explicativos, e fui direto
para a procedure DrawScreen. 


DrawScreen PROC
   MOV   SI, OFFSET Buffer             ; Aponta SI para o incio do buffer
   XOR   DI, DI                        ; Comea a desenhar em 0, 0
   MOV   BX, BufferY - 4               ; Perde as ltimas 4 linhas do buffer
                                       ; Essas linhas no se parecem
                                       ; com fogo, de jeito nenhum
Row:
   MOV   CX, BufferX SHR 1             ; 160 WORDS
   REP   MOVSW                         ; Move-as
   SUB   SI, 320                       ; Volta pro incio da linha do array
   MOV   CX, BufferX SHR 1             ; 160 WORDS
   REP   MOVSW                         ; Move-as
   DEC   BX                            ; Decrementa o nmero de linhas VGA restantes
   JNZ   Row                           ; Terminamos?
   RET
DrawScreen ENDP


Isto tambm  fcil seguir, e tira proveito de MOVSW, usando-a para mover dados entre DS:SI 
e ES:DI. 


AveragePixels PROC
   MOV   CX, BufferX * BufferY - BufferX * 2  ; Altera todo o buffer,
                                              ; exceto a primeira linha e a ltima
   MOV   SI, OFFSET Buffer + 320              ; Comea da segunda linha

Alter:
   XOR   AX, AX                        ; Zera AX
   MOV   AL, DS:[SI]                   ; Pega o valor do pixel atual
   ADD   AL, DS:[SI+1]                 ; Pega o valor do pixel  direita
   ADC   AH, 0
   ADD   AL, DS:[SI-1]                 ; Pega o valor do pixel  esquerda
   ADC   AH, 0
   ADD   AL, DS:[SI+BufferX]           ; Pega o valor do pixel abaixo
   ADC   AH, 0
   SHR   AX, 2                         ; Divide o total por quatro

   JZ    NextPixel                     ; O resultado  zero?
   DEC   AX                            ; No, ento decrementa de um



NOTA:  	O valor de decay (queda)  UM.  Se voc mudar a linha acima 
       	para, por exemplo "SUB AX, 2" voc vai ver que o fogo no chega to 
       	alto. Experimente... seja criativo!  :) 


NextPixel:
   MOV   DS:[SI-BufferX], AL           ; Pe o novo valor no array
   INC   SI                            ; Prximo pixel
   DEC   CX                            ; Um a menos para fazer
   JNZ   Alter                         ; J fizemos todos?
   RET
AveragePixels ENDP


Agora ns vimos a procedure que faz toda a suavizao. Basicamente, ns 
s temos um loop que soma os valores de cor dos pixels ao redor de um pixel, 
carregando os valores dos pixels antes. Quando ela tem o total em AX,
 dividido por quatro para conseguir uma mdia. A mdia  ento plotada 
diretamente sobre o pixel atual. 

Para mais informao relativo  instruo de ADC, observe isto em Tutorial 5, e 
olhe os programas abaixo: 

   Var                                     Var
      W : Word;                               W : Word;

   Begin                                   Begin
      Asm                                     Asm
         MOV  AL, 255                            MOV   AL, 255
         ADD  AL, 1                              ADD   AL, 1
         MOV  AH, 0                              MOV   W, AX
         ADC  AH, 0                           End;
         MOV  W, AX
      End;                                    Write(W);
                                           End;
      Write(W);
   End;

 ^^^ Este programa returna 256             ^^^ Este programa returna 0


Lembre-se de que ADC  usado para ter certeza que quando um registrador ou 
varivel no  grande bastante para armazenar um resultado, o resultado no ser 
perdido. 


OK, depois de pular algumas procedures um pouco mais irrelevantes, chegamos ao 
corpo principal do programa, que  algo desse tipo: 


Start:
   MOV   AX, @DATA
   MOV   DS, AX                        ; DS agora aponta para o segmento de dados.



Ns apontamos DS primeiramente para o segmento de dados, de modo que possamos ter 
acesso a todas nossas variveis. 


   CALL  InitializeMCGA
   CALL  SetUpPalette

MainLoop:
   CALL  AveragePixels

   MOV   SI, OFFSET Buffer + BufferX * BufferY - BufferX SHL 1
   ; SI agora aponta para o incio da segunda ltima linha (?????? - by Krull)
   MOV   CX, BufferX SHL 1             ; Prepara para pegar BufferX x 2 nmeros randmicos

BottomLine:
   CALL   Random                       ; Pega um nmero randmico
   MOV    DS:[SI], DL                  ; Usa apenas o byte baixo de DX, i.e.,
   INC    SI                           ; o nmero vai ser de 0 --> 255
   DEC    CX                           ; Um pixel a menos para fazer
   JNZ    BottomLine                   ; J acabamos?


Aqui, uma nova linha do fundo  calculada. O procedimento Random - muitas 
graas ao autor desconhecido da USENET - retorna um valor muito alto 
em DX:AX. Porm, ns s requeremos um nmero de 0 a 255, assim, usando s DL, 
ns temos tal nmero. 


   CALL  DrawScreen                    ; Copia o buffer para a VGA

   MOV   AH, 01H                       ; Checa se foi pressionada alguma tecla
   INT   16H                           ; H alguma tecla esperando no buffer?
   JZ    MainLoop                      ; No, segue em frente

   MOV   AH, 00H                       ; Sim, ento pega a tecla
   INT   16H

   CALL  TextMode
   MOV   AH, 4CH
   MOV   AL, 00H
   INT   21H                           ; Volta ao DOS
END Start


E eu acho que essa ltima parte tambm  bem fcil de entender. Eu tentei comentar 
o fonte o tanto quanto eu pude, talvez um pouco mais fortemente em algumas 
partes, mas eu espero que agora todo mundo tenha uma idia de como uma rotina de fogo 
funciona. 

De qualquer maneira, a meta era no lhe ensinar como fazer uma rotina de fogo, 
mas como usar arrays, assim se voc pegou o negcio do fogo tambm, ento 
isso  um bnus. Eu me referi ligeiramente diferentemente aos meus arrays de 
como eu expliquei neste tutorial, mas a teoria ainda  a mesma, e lhe mostra 
outros modos de fazer as coisas.  Se voc no entendeu como se usa arrays com isso 
tudo, ento talvez voc nunca entenda, pelo menos no com meu tutorials, sem dvida. 
Ei, vai compra um livro de $50!  :) 


  

O Tutorial de semana que vem ter: 

    E/S de arquivos 
    Usando Assembler com C/C++ 
    Tabelas de Lookup? 
    Macros. 

Se voc deseja ver um tpico discutido em um tutorial futuro, ento me escreva, 
e eu verei o que eu posso fazer. 


  

No perca!!! Pegue o tutorial da semana que vem da minha homepage em: 

   http://www.faroc.com.au/~blackcat 
   http://www.gecotiies.com/SiliconValley/Park/3174


At semana que vem!

- Adam. 
- Krull.
