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


Verso   :  1.1
Data     :  13-04-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


 

Ol de novo, programadores de Assembler. Esta edio demorou um pouco, mas eu
tinha muita coisa para terminar, e eu estou trabalhando em um jogo meu agora.
 um jogo de estratgia, como Warlords II, e acho que eu vou ter que escrever
a maior parte do cdigo em 640x480, no meu amado 320x200 - mas eu posso
mudar de idia. Eca, a quantidade de jogos que eu comecei a escrever mas
nunca cheguei a terminar  enorme, e este no deve chegar muito longe.

De qualquer modo, eu disse que daramos uma olhada numas rotinas de linha e
crculo esta semana, ento, vamos l...


 

Semana passada ns chegamos  seguinte rotina de linhas horizontais - 

      mov   ax, 0A000h
      mov   es, ax        ; Aponta ES para a VGA

      mov   ax, x1        ; AX = X1
      mov   bx, y         ; BX = Y
      mov   cx, x2        ; CX = X2

      sub   cx, ax        ; CX = Diferena de X2 e X1

      mov   di, ax        ; DI = X1
      mov   dx, bx        ; DX = Y
      shl   bx, 8         ; Y SHL 8
      shl   dx, 6         ; Y SHL 6
      add   dx, bx        ; DX = Y SHL 8 + Y SHL 6
      add   di, dx        ; DI = Offset do primeiro pixel

      mov   al, color     ; Pe a cor a plotar em AL
      rep   stosb         ; Desenha a linha


Agora, embora essa rotina seja muito mais rpida que as rotinas do BGI, (ou
seja l o que for que seu compilador tenha), ela poderia ser melhorada
pra caramba. Se entrarmos na rotina, com a lista de clocks que eu dei no
ltimo tutorial, voc vai ver que ela gasta bem pouco.

Eu vou deixar a otimizao com voc por enquanto, (vamos ver isso em outro
tutorial), mas se substituir STOSB por MOV ES:[DI], AL ou STOSW vai melhorar
muito as coisas. No se esquea que se voc decidir usar um loop,
para jogar words na VGA, voc ter que decrementar CX de uma unidade.

Agora, vamos ver uma linha vertical. Teremos que calcular o offset do primeiro
pixel como ns fizemos na rotina de linhas horizontais, ento, algo desse tipo
funcionaria:

   mov   ax, 0A000h      ; Pe o segmento VGA em AX
   mov   es, ax          ; Aponta ES para a VGA

   mov   ax, Y1          ; Move o primeiro valor de Y para AX
   shl   ax, 6           ; Y x 2^^6 (dois  sexta potncia)
   mov   di, ax          ; Move o novo valor de Y para DI
   shl   ax, 2           ; Agora temos Y = Y x 320
   add   di, ax          ; Adiciona aquele valor a DI
   add   di, X           ; Soma o valor de X a DI


Agora umas coisas bsicas...


   mov   cx, Y2          ; Guarda Y2 em CX
   mov   al, Color       ; Guarda a cor a plotar em AL
   sub   cx, Y1          ; CX = tamanho da linha


E agora o loop final...


Plota:
   mov   es:[di], al     ; Pe um pixel no offset corrente
   add   di, 320         ; Move para a prxima linha
   dec   cx              ; Decrementa CX de um
   jnz   Plota           ; Se CX <> 0, ento continua plotando


No  uma rotina fantstica, mas  muito boa. Note como foi possvel realizar
uma comparao depois de DEC CX. Isto  um conceito extremamente til, logo,
no se esquea de que isso  possvel.

Brinque um pouco com o cdigo, e tente faz-lo mais rpido. Tente outros
mtodos de calcular o offset, ou mtodos diferentes de controle de fluxo.

 

Agora, isso foi a coisa fcil. Vamos ver agora uma rotina capaz de desenhar 
linhas diagonais.

A seguinte rotina foi tirada de SWAG, autor desconhecido, e  uma rotina ideal
para demonstrar um algoritmo de linhas. Ele est precisando muito de uma
otimizao, assim, essa pode ser uma tarefa para voc - se voc quiser.
Alguns dos pontos a considerar so:

   1) Seja l quem o escreveu nunca ouviu falar de XCHG - isso economizaria
      alguns clocks;

   2) Ele comete um dos grandes pecados do cdigo no-otimizado - ele move
      um valor para AX, e ento realiza uma operao envolvendo AX na prxima
      instruo, assim fazendo um ciclo a mais. (Vamos falar sobre isso semana
      que vem).

   3) Ele trabalha com BYTES e no WORDS, assim, a velocidade de escrita para a
      VGA poderia se dobrada se usasse words.

   4) E o maior pecado de todos, ele usa um MUL para achar o offset. Tente
      usar shifts ou um XCHG para acelerar as coisas.

De qualquer modo, eu pus os comentrios nele, e acho que ele 
auto-explicativo, assim, eu no vou entrar em detalhes como ele funciona.
Voc deve ser capaz de pegar isso sozinho. Adentre a rotina, e veja como
a derivada (gradiente, variao, inclinao...) da linha foi calculada.


Procedure Line(X1, Y1, X2, Y2 : Word; Color : Byte);   Assembler;

Var
   DeX          : Integer;
   DeY          : Integer;
   IncF         : Integer;

Asm     { Line }
   mov   ax, [X2]      { Move X2 para AX                                    }
   sub   ax, [X1]      { Pega o tamanho horizontal da linha    (X2 - X1)    }
   jnc   @Dont1        { X2 - X1  negativo?                                }
   neg   ax            { Sim, ento faz com que seja positivo               }

@Dont1:
   mov   [DeX], ax     { Agora, move o tamanho horizontal da linha para DeX }
   mov   ax, [Y2]      { Move Y2 para AX                                    }
   sub   ax, [Y1]      { Subtrai Y1 de Y2, dando o tamanho vertical         }
   jnc   @Dont2        { Foi negativo?                                      }
   neg   ax            { Sim, ento faa-o positivo                         }

@Dont2:
   mov   [DeY], ax     { Move o tamanho vertica para DeY                    }
   cmp   ax, [DeX]     { Compara o tamanho vertivcal com o horizontal       }
   jbe   @OtherLine    { Se o vertical foi <= horizontal ento pula         }

   mov   ax, [Y1]      { Move Y1 para AX                                    }
   cmp   ax, [Y2]      { Compara Y1 a Y2                                    }
   jbe   @DontSwap1    { Se Y1 <= Y2 ento pula, seno...                   }
   mov   bx, [Y2]      { Pe Y2 em BX                                       }
   mov   [Y1], bx      { Pe Y2 em Y1                                       }
   mov   [Y2], ax      { Move Y1 para Y2                                    }
                       { Para que depois de tudo isso.....                  }
                       { Y1 = Y2 e Y2 = Y1                                  }

   mov   ax, [X1]      { Pe X1 em AX                                       }
   mov   bx, [X2]      { Pe X2 em BX                                       }
   mov   [X1], bx      { Pe X2 em X1                                       }
   mov   [X2], ax      { Pe X1 em X2                                       }

@DontSwap1:
   mov   [IncF], 1     { Pe 1 em IncF, i.e., plota outro pixel             }
   mov   ax, [X1]      { Pe X1 em AX                                       }
   cmp   ax, [X2]      { Compara X1 com X2                                  }
   jbe   @SkipNegate1  { Se X1 <= X2 ento pula, seno...                   }
   neg   [IncF]        { Nega IncF                                          }

@SkipNegate1:
   mov   ax, [Y1]      { Move Y1 para AX                                    }
   mov   bx, 320       { Move 320 para BX                                   }
   mul   bx            { Multiplica 320 por Y1                              }
   mov   di, ax        { Pe o resultado em DI                              }
   add   di, [X1]      { Soma X1 a DI, e tcham - offset em  DI              }
   mov   bx, [DeY]     { Pe DeY em BX                                      }
   mov   cx, bx        { Pe DeY em CX                                      }
   mov   ax, 0A000h    { Pe o segmento a ser plotado, em AX                }
   mov   es, ax        { ES aponta para a VGA                               }
   mov   dl, [Color]   { Pe a cor a usar em DL                             }
   mov   si, [DeX]     { Aponta SI para DeX                                 }

@DrawLoop1:
   mov   es:[di], dl   { Pe a cor a plotar, DL, em ES:DI                   }
   add   di, 320       { Soma 320 a DI, i.e., prxima linha abaixo          }
   sub   bx, si        { Subtrai DeX de BX, DeY                             }
   jnc   @GoOn1        { Ficou negativo?                                    }
   add   bx, [DeY]     { Sim, ento soma DeY a BX                           }
   add   di, [IncF]    { Soma a quantidade a incrementar a DI               }

@GoOn1:
   loop  @DrawLoop1    { Nenhum resultado negativo, ento plota outro pixel }
   jmp   @ExitLine     { Acabou, ento vamos embora!                        }

@OtherLine:
   mov   ax, [X1]      { Move X1 para AX                                    }
   cmp   ax, [X2]      { Compara X1 a  X2                                   }
   jbe   @DontSwap2    { X1 <= X2 ?                                         }
   mov   bx, [X2]      { No, ento move X2 para BX                         }
   mov   [X1], bx      { Move X2 para X1                                    }
   mov   [X2], ax      { Move X1 para X2                                    }
   mov   ax, [Y1]      { Move Y1 para AX                                    }
   mov   bx, [Y2]      { Move Y2 para BX                                    }
   mov   [Y1], bx      { Move Y2 para Y1                                    }
   mov   [Y2], ax      { Move Y1 para Y2                                    }

@DontSwap2:
   mov   [IncF], 320   { Move 320 para IncF, i.e., o prximo pixel est na  }
                       { prxima linha                                      }
   mov   ax, [Y1]      { Move Y1 para AX                                    }
   cmp   ax, [Y2]      { Compara Y1 a  Y2                                   }
   jbe   @SkipNegate2  { Y1 <= Y2 ?                                         }
   neg   [IncF]        { No, ento nega IncF                               }

@SkipNegate2:
   mov   ax, [Y1]      { Move Y1 para AX                                    }
   mov   bx, 320       { Move 320 para BX                                   }
   mul   bx            { Multiplica AX por 320                              }
   mov   di, ax        { Move o resultado para DI                           }
   add   di, [X1]      { Soma X1 a DI, dando o  offset                      }
   mov   bx, [DeX]     { Move DeX para BX                                   }
   mov   cx, bx        { Move BX para CX                                    }
   mov   ax, 0A000h    { Move o endereo da VGA para AX                     }
   mov   es, ax        { Aponta ES para a VGA                               }
   mov   dl, [Color]   { Move a cor a plotar para DL                        }
   mov   si, [DeY]     { Move DeY para SI                                   }

@DrawLoop2:
   mov   es:[di], dl   { Pe o byte em DL para ES:DI                        }
   inc   di            { Incrementa DI de um, o prximo pixel               }
   sub   bx, si        { Subtrai SI de BX                                   }
   jnc   @GoOn2        { Ficou negativo?                                    }
   add   bx, [DeX]     { Sim, ento soma DeX a BX                           }
   add   di, [IncF]    { Soma IncF a DI                                     }

@GoOn2:
   loop  @DrawLoop2    { Continua plotando                                  }

@ExitLine:
                       { Pronto!                                            }
End;


Acho que no fiz nenhum erro com os comentrios, mas eu estou bem cansado,
e no tenho bebido cafena h dias - se voc encontrar um erro - por favor
me diga.

Eu ia colocar um algoritmo de crculo, mas eu no consegui fazer a minha
funcionar em Assembler - toda aquela matemtica de ponto flutuante deve ter
algo a ver com isso. Eu poderia incluir uma escrita em linguagem de alto
nvel, mas eu suponho que esse seja um tutorial de Assembler, no de grficos.
Contudo, se pessoas suficientes reclamarem, querendo uma...


 

         
                                                                   
                         OS INS E OUTS DE IN E OUT                 
                                                                   
         


IN e OUT so uma parte muito importante de cdigo em Assembler. Elas permitem
voc a mandar/receber diretamente dados de qualquer uma das  65,536 portas de
hardware, ou registradores. A sintaxe bsica  como segue:


    IN <ACUMULADOR>, <PORTA>     - Nome: Entrada de porta de E/S
                                    Tipo: 8086+

                                    Descrio: Esta instruo l um valor
                                    de uma das 65536 portas de hardware 
                                    para o acumulador especificado.

                                    AX e AL so comumente usados para portas
                                    de entrada, e DX  mais usado para
                                    identificar a porta.

                                    EX.: IN    AX, 72h

                                         MOV   DX, 3C7h
                                         IN    AL, DX


    OUT <PORTA>, <ACUMULADOR>    - Nome: Sada para a Porta
                                    Tipo: 8086+

                                    Descrio: Esta instruo pe na sada o 
                                    valor no acumulador para <PORTA>.  Usando
                                    o registrador DX para passar a porta para
                                    OUT, voc pode acessar at 65,536 portas.

                                    EX.: MOV   DX, 378h
                                         OUT   DX, AX


OK, isso no ajudou muito, j que no disse muito sobre como usar - muito
menos para que usar. Bem, se voc pretende trabalhar muito com a VGA, voc
ter que ser capaz de programar seus registradores internos. Semelhantes
aos registradores com que voc tem trabalhado at agora, voc pode pensar
em mud-los como interrupes, exceto que: 1) Voc passa os valores para a
porta, e  isso a; e 2)  muito perto de ser instantneo.

Como exemplo, vamos ver como setar e pegar a palette controlando diretamente
o hardware da VGA.


Agora, a VGA tem uma poro de registradores, mas as prximas trs  bom que
voc conhea bem:

    03C7h       - PEL Registrador de Endereos (Leitura)
                   Seta a palette em mode de leitura

    03C8h       - PEL Registrador de Endereos (Escrita)
                   Seta a palette em modo de escrita

    03C9h       - PEL Registrador de Dados (Leitura/Escrita)
                   L, ou escreve 3 valores RGB, a cada terceira escrita, o
                   ndice, ou cor que voc est setando,  incrementado de um.

O que tudo isso significa  -

Se ns fssemos setar um valor RGB de uma cor RGB, ns mandaramos o valor da
cor que queramos mudar para 03C8h, ento ler os 3 valores de 03C9h. Em
Assembler, faramos isso:

   mov   dx, 03C8h        ; Pe o registrador DAC de leitura em DX
   mov   al, [Color]      ; Pe o valor da cor em AL
   out   dx, al           ; Manda AL para a porta DX
   inc   dx               ; Agora usa a porta 03C9h
   mov   al, [R]          ; Pe o novo valor VERMELHO em AL
   out   dx, al           ; Manda AL para a porta DX
   mov   al, [G]          ; Pe o novo valor VERDE em AL
   out   dx, al           ; Manda AL para a porta DX
   mov   al, [B]          ; Pe o novo valor AZUL em AL
   out   dx, al           ; Manda AL para a porta DX

E aquilo deveria fazer a coisas direitinho. Para ler a palette, faramos isso:

   mov   dx, 03C7h        ; Pe o registrador DAC de escrita em DX
   mov   al, [Color]      ; Pe o valor da cor em AL
   out   dx, al           ; Manda AL para a porta DX
   add   dx, 2            ; Agora usa a porta 03C9h

   in    al, dx           ; Pe o valor conseguido da porta DX em AL
   les   di, [R]          ; Aponta DI para a varivel R - Isso vem do Pascal
   stosb                  ; Guarda AL em R

   in    al, dx           ; Pe o valor conseguido da porta DX em AL
   les   di, [G]          ; Aponta DI para a varivel G
   stosb                  ; Guarda AL em G

   in    al, dx           ; Pe o valor conseguido da porta DX em AL
   les   di, [B]          ; Aponta DI para a varivel B
   stosb                  ; Guarda AL em B

Note como essa rotina foi codificada diferentemente. Isso era originalmente
uma rotina em Pascal, e como o Pascal no gosta que voc mexa com variveis
de Pascal em Assembler, voc tem que improvisar.

Se voc est trabalhando com Assembler puro, ento voc pode codificar
isso muito mais eficientemente, como o primeiro exemplo. Eu deixei o cdigo
como estava para que aqueles trabalhando com uma linguagem de alto nvel
possam chegar a um problema particularmente irritante.

Agora voc j viu como IN e OUT podem ser teis. Controlar diretamente o
hardware  mais rpido e mais eficiente. Nas prximas semanas, eu posso
incluir uma lista das portas mais comuns, mas se voc tivesse uma cpia da
Ralf Brown's Interrupt List (disponvel no X2FTP), voc j teria uma cpia.


OBS.: Voc pode achar um link para a Ralf Brown's Interrupt List na minha
      pgina.


 

 Um pouco mais sobre o registrador de flags:


Agora, embora tenhamos usado o registrador de flags em quase todo nosso
cdigo at esse ponto, eu no entrei profundamente nesse assunto. Voc pode
trabalhar felizmente sem conhecer muito sobre os flags, e comparar coisas
sem saber o que est realmente acontecendo, mas se voc quiser avanar no
Assembler, voc precisa saber algo mais.

De volta ao Tutorial Trs, eu dei uma viso simplista do registrador de FLAGS.
Na realidade, os FLAGS, ou EFLAGS  na verdade um registrador de 32-bit,
embora apenas s os bits de 0 a 18 sejam usados. Na realidade no precisamos
conhecer os flags acima do 11 por enquanto, mas  bom saber que eles existem.


O registrador EFLAGS na verdade se parece com isso:

18  17  16  15  14  13  12  11  10  09  08  07  06  05  04  03  02  01  00
AC  VM  RF  --  NT  IO/PL   OF  DF  IF  TF  SF  ZF  --  AF  --  PF  --  CF


Agora, os flags so os seguintes:

    AC   - Alignment Check (80486) / Checagem de Alinhamento
    VM   - Virtual 8086 Mode / Modo Virtual 8086
    RF   - Resume Flag / Flag de Continuao
    NT   - Nested Task Flag / Flag de Tarefa Aninhada
    IOPL - I/O Privilege Level / Nvel de Privilgio de E/S
            Tem um valor de 0,1,2 ou 3 logo ocupa 2 bits

    OF   - Overflow Flag / Flag de Overflow
            Este bit  setado para UM se uma instruo aritmtica gerar um
            resultado que  muito grande ou muito pequeno para caber no
            registrador destino.

    DF   - Direction Flag / Flag de Direo
            Qaundo setado para ZERO, as instrues de string, como MOVS, LODS,
            e STOS incrementaro o endereo de memria que elas esto
            trabalhando de uma unidade. Isto significa que, digamos, DI ser
            incrementado quando voc usar STOSB para colocar um pixel em
            ES:DI. Setando o bit para UM decrementar o endereo de memria
            aps cada chamada.

    IF   - Interrupt Enable Flag / Flag de Habilitao de Interrupes
            Quando este bit est setado, o processador responder a
            interrupes externas do hardware. Quando o bit for resetado,
            interrupes de hardware so ignoradas.

    TF   - Trap Flag / Flag de Trap ("armadilha")
            Quando este bit estiver setado, uma interrupo ocorrer
            imediatamente depois que a prxima instruo executar. Isto 
            geralmente usado em depuraes.

    SF   - Sign Flag / Flag de Sinal
            Este bit  mudado aps operaes aritmticas. O bit recebe
            o bit de mais alta ordem do resultado, e se setado para UM,
            indica que o resultado da operao foi negativo.

    ZF   - Zero Flag / Flag de Zero
            Este bit  setado quando instrues aritmticas geram um
            resultado zero.

    AF   - Auxiliary Carry Flag / Flag de Vai-Um Auxiliar
            Este bit indica que um vai-um no nibble de baixa ordem de AL
            ocorreu na instruo aritmtica.

    PF   - Parity Flag / Flag de Paridade
            Este bit  setado para um quando uma instruo aritmtica resulta
            num nmero par de bits 1.

    CF   - Carry Flag / Flag de Vai-Um
            Este bit  setado quando o resultado de uma operao aritmtica
             muito grande ou muito pequena para o registrador destino
            ou endereo de memria.


Agora, de todos esses acima, voc no precisa mesmo se preocupar muito com
a maioria deles. Por enquanto, s conhecer CF, PF, ZF, IF, DF e OF ser
suficiente. Eu no dei comentrios para os primeiros j que eles so
puramente tcnicos, e so usados mais no modo protegido e situaes complexas.
Voc no deveria ter que conhec-los.

Voc pode, se quiser, mover uma copia do flags para AH com LAHF - (Carrega AH
com Flags) - e modificar ou ler bits individualmente, ou mudar o status dos
bits mais facilmente com CLx e STx. Contudo se voc planeja mudar os flags,
lembre-se de que eles podem ser extremamente teis em muitas situaes.

(Eles podem tambm ser muito enjoados quando tarde da noite, linhas comeam
a desenhar para trs, e voc gasta um hora procurando o porqu - e ento se
lembra que voc se esqueceu de limpar o flag de direo!)


 

Acho que cobrimos muito pouca coisa importante neste tutorial. D uma olhada
nos flags, e volte naquela rotina compridona de fazer linhas, j que ela 
um timo exemplo de controle de fluxo. Assegure-se de que suas capacidades de
controlar fluxos de intrues esto perfeitas.

Semana que vem, vou tentar amarrar todos os tpicos que vimos estas poucas
semanas juntos, e apresentar alguma forma de reviso de tudo que voc
aprendeu. Semana que vem eu tambm vou entrar em otimizao, e como voc
pode acelerar todo o cdigo com que temos trabalhado at agora.


 

No prximo tutorial vamos ver:

    Uma reviso  de tudo que voc aprendeu
    Otimizao
    Declarando procedures (procedimentos) em Assembler
    Ligando seu cdigo a C/C++ ou Pascal

Se voc deseja ver um tpico discutido num tutorial no futuro, escreva-me, e
eu vou ver o que eu posso fazer.


 

No perca!!! Baixe o tutorial da prxima semana na minha homepage:

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


Vejo vocs na prxima semana!

- Adam.
- Renato Nunes Bastos
