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


Verso   :  1.3
Data     :  01-03-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-vindos mais uma vez, florescentes programadores Assembler. Os tutoriais
parecem estar ficando populares agora, e eu tenho recebido e-mails pedindo-me
para falar sobre o VGA, ento eu vou dar olhada. Isso  basicamente para onde
eu tenho conduzido no meu modo desconjuntado, de qualquer modo, j que
programao grfica no  s recompensante,  divertido tambm! Bem, eu
acho que .  :)

Primeiramente porm, devemos terminar aquela coisa de CMP/JMP, e falar de
shifts. Quando se est programando em Assembler, a gente acha que comparaes,
shifts e testar bits so operaes muito comuns.


 

 Um Exemplo de Comparao
--------------------------

Eu no vou perder tempo explicando minuciosamente o seguinte exemplo - ele
 muito fcil de entender e voc deve pegar a idia basica seja l como for.


   DOSSEG
   .MODEL SMALL
   .STACK 200h
   .DATA

FirstString    DB  13, 10, "Este  um grande tutorial ou o qu? :) - $"
SecondString   DB  13, 10, "NO? NO? O que voc quer dizer, NO?$"
ThirdString    DB  13, 10, "Excelente, vamos ouvir voc dizer isso de novo.$"
FourthString   DB  13, 10, "Apenas um Y ou N j basta.$"
ExitString     DB  13, 10, "Bem, deixa pra l!$"

   .CODE

START:
   MOV   AX, @DATA                   ; Novo modo de dizer:
   MOV   DS, AX                      ; DS -> SEG segmento de dados

KeepOnGoing:
   MOV   AH, 9
   MOV   DX, OFFSET FirstString      ; DX -> OFFSET FirstString
   INT   21h                         ; Escreve a primeira mensagem

   MOV   AH, 0                       ; Pega uma tecla - armazena-a em AX
   INT   16h                         ; AL - cdigo ASCII, AH - "scan code"
                                     ; Ela no ecoa na tela, contudo,
                                     ; ns mesmos temos que fazer isso.

   PUSH  AX                          ; Aqui ns mostramos na tela o caracter
   MOV   DL, AL                      ; note que ns salvamos AX. Obviamente, 
   MOV   AH, 2                       ; usando-se AH para imprimir uma string 
   INT   21h                         ; destri-se AX
   POP   AX

   CMP   AL, "Y"                     ; Checa se foi teclado 'Y' 
   JNE   HatesTute                   ; Se foi, continua

   MOV   AH, 9                       ; Mostra a mensagem "Excelente..." 
   MOV   DX, OFFSET ThirdString
   INT   21h
   JMP   KeepOnGoing                 ; Volta ao incio e comea de novo

HatesTute:
   CMP   AL, "N"                     ; Certifica que foi teclado 'N'
   JE    DontLikeYou                 ; Infelizmente, sim.

   MOV   DX, OFFSET FourthString     ; Pede ao usurio para tentar de novo
   MOV   AH, 9
   INT   21h
   JMP   KeepOnGoing                 ; Deixa ele tentar

DontLikeYou:
   MOV   DX, OFFSET SecondString     ; Mostra a string "NO? NO? O que..."
   MOV   AH, 9
   INT   21h

   MOV   DX, OFFSET ExitString       ; Mostra a string "Bem, deixa pra l!" 
   MOV   AH, 9
   INT   21h

   MOV   AX, 4C00h                   ; Volta para o DOS
   INT   21h
END START

Voc deveria entender este exemplo, brincar um pouco com ele e escrever
algo melhor. Aqueles com um livro do Peter Norton ou algo semelhante,
experimentem as subfunes do teclado, e veja quais outras combinaes de
GetKey existem, ou melhor ainda, brinque com a interrupo 10h e entre em
algum modo de vdeo sobrenatural - um que seu PC suporte! - e use algumas
cores.


 

 Shifts
--------

Um simples conceito, e um que eu j devia ter discutido antes, mas como eu
disse - eu tenho minha prpria maneira desconjuntada de fazer as coisas.

Primeiro voc vai precisar de entender um pouco de aritmtica hexadecimal e
binria - um assunto que eu _deveria_ ter coberto antes. Eu geralmente uso
uma calculadora cientfica - ei, eu sempre uso uma calculadora, eu no sou
estpido! - mas  bom ser capaz de saber como multiplicar, somar e converter
entre as vrias bases.

Voc tambm no pode usar uma calculadora em provas de Computao, no na
Austrlia.


 CONVERTENDO DE BINRIO PARA DECIMAL:

De Volta ao Tutorial Um, ns vimos como nmeros binrios se parecem, ento
imagine que eu tenha um nmero binrio de oito dgitos, como:

 11001101

O que  isso em decimal??? H vrias formas de converter tal nmero, e eu
uso a seguinte, que acredito se provavelmente a mais fcil:

 ͻ
   Nmero Binrio         1    1   0   0   1   1   0   1  
 Ķ
                           7   6   5   4   3   2   1   0  
   Equivalente Decimal    2   2   2   2   2   2   2   2   
 Ķ
   Equivalente Decimal   128  64  32  16   8   4   2   1  
 ķ
   Valor Decimal         128 + 64 +  0 +  0 +  8 +  4 +  0 +  1  = 205 
 ͼ

Pegou a idia? Note que para a ltima linha, seria mais preciso escrever:

   1 x 128 + 1 x 64 + 0 x 32 + 0 x 16 + 1 x 8 + 1 x 4 + 0 x 2 + 1 x 1
 =     128 +     64 +      0 +      0 +     8 +     4 +     0 +     1
 = 205

Desculpe se isto  um pouco confuso, mas  difcil explicar sem demonstrar.
Aqui vai outro exemplo:

 ͻ
   Nmero Binrio         0    1   1   1   1   1   0   0  
 Ķ
                           7   6   5   4   3   2   1   0  
   Equivalente Decimal    2   2   2   2   2   2   2   2   
 Ķ
   Equivalente Decimal   128  64  32  16   8   4   2   1  
 ķ
   Valor Decimal          0  + 64 + 32 + 16 +  8 +  4 +  0 +  0  = 124 
 ͼ

Obs.:

    Voc pode usar esta tcnica com palavras de 16 ou 32 bits tambm, apenas
     faa do jeito certo. Ex: Depois de 128, voc escreveria 256, depois 512,
     1024 e assim por diante.

    Voc pode dizer se o equivalente decimal serpar ou mpar pelo primeiro
     bit.  Ex.: No exemplo acima, o primeiro bit = 0, ento o nmero  PAR.
     No primeiro exemplo, o primeiro bit  1, ento o nmero  MPAR.

FATO ENGRAADO: Caso voc no saiba ainda, bit vem de Binary digIT.  :)


 CONVERTENDO DE DECIMAL PARA BINRIO:

Isso  provavelmente mais fcil que da base-2 para base-10. Para calcular
o que 321 seria em binrio, voc faria o seguinte:

    321                    =    256  X  1
    321 - 256 = 65         =    128  X  0
    65                     =     64  X  1
    65  -  64 = 1          =     32  X  0
    1                      =     16  X  0
    1                      =      8  X  0
    1                      =      4  X  0
    1                      =      2  X  0
    1                      =      1  X  1

E voc obteria o nmero binrio - 101000001. Fcil, n?  Vamos tentar outro
para ter certeza que sabemos fazer:

    198                    =    128  X 1
    198 - 128 = 70         =     64  X 1
    70  -  64 =  6         =     32  X 0
    6                      =     16  X 0
    6                      =      8  X 0
    6                      =      4  X 1
    6   -   4 =  2         =      2  X 1
    2   -   2 =  0         =      1  X 0

E isto nos d - 11000110.  Note como voc pode checar o primeiro dgito para
ver se voc conseguiu sua converso certa. Quando eu escrevi o primeiro
exemplo, eu notei que eu fiz um erro quando eu chequei o primeiro bit. No
primeiro exemplo, eu consegui 0 - no muito bom para um nmero mpar.
Eu entendi o erro e corrigi o exemplo.


 CONVERTENDO DE HEXADECIMAL PARA DECIMAL:

Antes de comear, voc deveria saber que o sistema numrico hexadecimal usa os
'dgitos':

   0         =  0 (decimal)  =     0 (binrio)
   1         =  1 (decimal)  =     1 (binrio)
   2         =  2 (decimal)  =    10 (binrio)
   3         =  3 (decimal)  =    11 (binrio)
   4         =  4 (decimal)  =   100 (binrio)
   5         =  5 (decimal)  =   101 (binrio)
   6         =  6 (decimal)  =   110 (binrio)
   7         =  7 (decimal)  =   111 (binrio)
   8         =  8 (decimal)  =  1000 (binrio)
   9         =  9 (decimal)  =  1001 (binrio)
   A         = 10 (decimal)  =  1010 (binrio)
   B         = 11 (decimal)  =  1011 (binrio)
   C         = 12 (decimal)  =  1100 (binrio)
   D         = 13 (decimal)  =  1101 (binrio)
   E         = 14 (decimal)  =  1110 (binrio)
   F         = 15 (decimal)  =  1111 (binrio)

Voc vai comumente ouvir hexadecimal referenciado como hex, ou base-16 e ela
 comumente denotada por um 'h' - ex.: 4C00h, ou um '$', ex.: - $B800.

Trabalhar com hexadecimal no  to difcil como pode parecer, e converter
pra l ou pra c  bem fcil. Como exemplo, vamos converter B800h para
decimal:

FATO ENGRAADO: B800h  o endereo inicial do vdeo em modo texto para CGA e
          placas superiores.  :)

          B    = 4096 x B = 4096 x 11 = 45056
          8    =  256 x 8 =  256 x  8 =  2048
          0    =   16 x 0 =   16 x  0 =     0
          0    =    1 x 0 =    1 x  0 =     0

          Logo B800h = 45056 + 2048 + 0 + 0
                     = 47104

          Obs.:  Para nmeros em hexadecimal maiores que FFFFh (65535 em
                 decimal), voc somente segue o mesmo procedimento como para
                 binrio, logo, para o quinto dgito hexadecimal, voc
                 multiplicaria por 65535.

                 Tecle 16 X X na sua calculadora, e fique apertando =.
                 Voc ver os nmeros que precisaria usar. O mesmo aplica-se
                 para binrio. Ex.:  2 X X e = lhe daria 1, 2, 4, 8, 16...
                 etc.

OK, isso pareceu bem fcil. Eu acho que nem precisamos de um segundo exemplo.
Vamos dar uma olhada em:


 CONVERTENDO DE DECIMAL PARA HEXADECIMAL:

Mais uma vez, o mesmo tipo de procedimento como usamos para binrio. Logo,
para converter 32753 para hexadecimal, voc faria assim:


          32753 / 4096                  =  7 (decimal) = 7h

          32753 - (4096 x 7) = 4081

          4081 /  256                   = 15 (decimal) = Fh

          4081 - (256 x 15)  =  241

          241 / 16                      = 15 (decimal) = Fh

          241 - (16 x 15)    = 1

          1 / 1                         =  1 (decimal) = 1h


Assim, eventualmente temos 7FF1h como resposta. Este no  particularmente
um bom processo e requer alguma explicao.

   1) Quando voc divide 32753 por 4096 voc consegue 7.9963379... No estamos
      interessados no lixo .9963379, s pegamos o 7, j que 7  o maior
      nmero inteiro que podemos usar.

   2) O resto da operao acima  4081. Devemos agora realizar a mesma
      operao nisso, mas com 256.  Dividindo 4081 por 256 nos d
      15.941406...  Novamente, pegamos s o 15.

   3) Agora temos um resto de 241.  Dividindo isto por 16 nos d 15.0625.
      Pegamos o 15, e calculamos o resto.

   4) Nosso ltimo resto acontece que  um. Dividindo isso por um chegamos a,
      voc advinhou - um.  VOC NO DEVERIA CONSEGUIR UMA RESPOSTA COM MUITAS
      CASAS DECIMAIS AQUI. SE VOC TEM - VOC CALCULOU ERRADO.

 um processo muito imundo, mas funciona.  Eu no uso isso, exceto quando
eu tenho que usar - eu no sou maluco. Eu uso uma calculadora cientfica,
ou a calculadora do Windows <brrrrr> se eu precisar.


 

OK, agora que j lidamos com os clculos horripilantes, voc j est pronto
para os shifts. H geralmente 2 formas da instruo shift - SHL (shift
left/esquerda) e SHR (shift right/direita). Basicamente, tudo o que
essas instrues fazem  deslocar uma expresso para a esquerda ou direita
um certo nmero de bits. Sua principal vantagem  a habilidade de lhe deixar
substituir multiplicaes lentas com shifts mais rpidos. Voc vai achar que
isso acelerar pra caramba os algoritmos de pixel/linhas/crculo. 

Os PC's esto ficando cada vez mais rpidos a cada dia - um pouco rpido
demais pro meu gosto. De volta aos dias do XT - a multiplicao era
_realmente_ lenta - talvez levando at 4 segundos para certas operaes. Hoje
em dia isso no acontece assim, mas  uma boa idia otimizar seu cdigo.

Quando ns plotamos um pixel na tela, temos que encontar o offset do pixel
a plotar. Basicamente, o que fazemos  multiplicar a posio Y por 320, somar
a posio X, e somar isso ao endereo A000h.

Assim basicamente, temos:   A000:Yx320+X

Agora, seja l quo rpido seu maravilhoso 486 ou Pentium ,
isso poderia se feito um pouco mais rpido. Vamos reescrever aquela equao
acima, assim, vamos usar alguns nmeros diferentes:

                            8          6
              Offset = Y x 2   +  Y x 2  + X
Ou:
              Offset = Y x 256 +  y x 64 + X

Reconhece esses nmeros? Eles parecem terrivelmente com aqueles
que ns vimos naquela tabela de converso binrio-decimal.
Contudo, ns ainda estamos usando multiplicao.
Como podemos incorporar shifts?

Que tal:

              Offset = Y SHL 8 + Y SHL 6 + X

Agora, isso  _muito_ mais rpido, j que tudo o que o computador tem que
fazer  um shift  esquerda com o nmero - muito melhor.
Note que o shift  esquerda AUMENTA o nmero, e o shift  direita
DIMINUI o nmero.

Aqui est um exemplo que pode te ajudar se voc ainda est em dvida no que
est acontecendo. Digamos que estamos trabalhando em base-10 - decimal. Agora
Peguemos o nmero 36 como exemplo. "Shiftando" este nmero  esquerda de 1,
temos:

  36  +  36 							  = 72

Agora SHL 2:

  36  +  36  +  36  +  36                               = 144

E SHL 3:
  36 +  36   +  36  +  36  +  36  +  36  +  36  +  36   = 288

Notou os neros que se formaram? Havia 2 36's com SHL 1, 4 36's com SHL 2
e 8 36's com SHL 3. Seguindo este padro, seria justo assumir que 36 SHL 4
equivaler a 36 x 16.

Note porm, o que est realmente acontecendo. Se voc fosse trabalhar com
o valor binrio de 36, que  mais ou menos isso: 100100, e ento shiftasse 36
 esquerda de 2, voc teria 144, ou 10010000. Tudo o que a CPU faz na verdade
 colocar alguns 1's e 0's extras na posio de memria.


Como outro exemplo, pegue o nmero binrio 1000101. Se fizermos um shift 
esquerda de 3, terminaramos com:

        1 0 0 0 1 0 1
          <---------- SHL 3
  1 0 0 0 1 0 1 0 0 0

Agora vamos deslocar o nmero 45  DIREITA de 2 unidades. Em binrio isso 
101101. De onde:

        1 0 1 1 0 1
        SHR 2 ---->
            1 0 1 1

Notou o que ocorreu?  muito mais fcil para a CPU apenas mover alguns bits
(aproximadamente 2 unidades de clock), do que multiplicar um nmero. (Pode
demorar at 133 unidades de clock).

Ns vamos usar bastante shifts quando estivermos programando a VGA, assim,
tenha certeza de que voc entendeu os conceitos por trs disso.


 

         Ŀ
                                                                   
                      PROGRAMANDO A VGA EM ASSEMBLER               
                                                                   
         


Eu tenho recebido um monte de mails me pedindo para cobrir a VGA. Ento, para
todos os que pediram, ns estaremos gastando a maior parte do nosso tempo,
mas no todo, em programar a VGA. Alm do mais, no querem todos programar
com grficos?

Quando ns falamos sobre programar a VGA, ns estamos geralmente falando do
modo 13h, ou um de seus parentes. Em VGA padro este  o _nico_ modo 
de usar 256 cores, e  provavelmente um dos modos mais fceis tambm.
Se voc j tentou experincias com a SVGA, voc vai entender o pesadelo
que  para o programador dar suporte a todas as diferentes placas SVGA que
existem - exceto se voc usar VESA que  o que discutiremos outra hora. A
grande vantagem do modo padro 13h  que voc sabe que todas as placas VGA
que existem vo suport-lo. As pessoas hoje frequentemente ignoram o modo 13h,
achando a resoluo muito granulada para os padres de hoje, mas no se esquea
que Duke Nukem, DOOM, DOOM II, Halloween Harry e a maioria dos jogos da Apogee
usam este modo para realizar alguns grandes efeitos.

A grande coisa sobre o modo 13h - isto  320x200x256 caso voc desconhea,
 que acessar a VGA RAM  incrivelmente fcil. Como 320 x 200  igual a 
64,000,  possvel encaixar a tela inteira em um segmento de 64K.

As ms notcias so que o modo padro 13h realmente s te d uma pgina para
usar, seriamente embaraante para scroll e page-flipping. ns vamos cobrir
mais tarde estes assuntos, como entrar em seus prprios modos - e modo X que
evitar esses problemas.


Ento, como entrar no modo padro 13h?

A resposta  simples. Usamos a interrupo 10h - interrupo de vdeo,
e chamamos a subfuno 00h - seleciona o modo. Em Pascal, voc poderia
declarar uma procedure como esta:

Procedure Init300x200;   Assembler;

Asm     { Init300x200 }
   mov   ah, 00h         { Acerta o modo de vdeo }
   mov   al, 13h         { Usa o modo 13h         }
   int   10h             { Faz isso               }
End;    { Init300x200 }


voc tambm pode ver:

   mov   ax, 13h
   int   10h

Isso  perfeitamente correto, e provavelmente economiza um tempo de clock por
no colocar 00h em AH e ento 13h em AL, mas  mais correto usar o primeiro
exemplo.


OK, ento estamos no modo 13h, mas o que podemos realmente fazer nele, alm
de olhar para uma tela em branco? Poderamos voltar ao modo texto usando:

   mov   ah, 00h
   mov   al, 03h
   int   10h

Mas isso  um pouco idiota. Porque no pintar um pixel?


 

H inmeros modos de colocar um pixel na tela. O modo mais fcil em Assembler
 usar interrupes. Voc faria mais ou menos assim em Pascal:

Procedure PutPixel(X, Y : Integer; Color : Byte);   Assembler;

Asm     { PutPixel }
   mov   ah, 0Ch        { subfuno de desenhar pixel   }
   mov   al, [Color]    { Move a cor a plotar para AL   }
   mov   cx, [X]        { Move o valor X para CX        }
   mov   dx, [Y]        { Move o valor Y para DX        }
   mov   bx, 1h         { BX = 1, pgina 1              }
   int   10h            { Plota                         }
End;    { PutPixel }


Contudo, mesmo isso sendo em Assembler, no  particularmente rpido. Por qu?,
voc pergunta. Porque isso usa interrupo. Interrupes so timas para entar
e sair de modos de vdeo, ligar e desligar o cursor, etc... mas no para
grficos.

Voc pode imaginar interrupes como uma secretria eletrnica. "A CPU est
ocupada neste momento, mas se voc deixar sua subfuno aps o sinal - ns
entraremos em contato."

No  bom. Vamos usar a tcnica que discutimos anteriormente durante shifts.
O que queremos fazer  botar o valor da cor que desejamor plotar na VGA
diretamente. Para fazer isso, precisamos mover o endereo da VGA para ES,
e calcular o offset do pixel que queremos plotar. Um exemplo disso  mostrado
abaixo:

Procedure PutPixel(X, Y : Integer; Color : Byte);   Assembler;

Asm     { PutPixel }
   mov   ax, 0A000h     { Move o segmento da VGA para AX,            }
   mov   es, ax         { e agora para ES                            }
   mov   bx, [X]        { Move o valor X para BX                     }
   mov   dx, [Y]        { Move o valor Y para DX                     }
   mov   di, bx         { Move X para DI                             }
   mov   bx, dx         { Move Y para BX                             }
   shl   dx, 8          { Nesta parte usamos shifts para multiplicar }
   shl   bx, 6          { Y por 320                                  }
   add   dx, bx         { Agora somamos X ao valor acima calculado,  }
   add   di, dx         { dando DI = Y x 320 + X                     }
   mov   al, [Color]    { Pe a cor a plotar em AL                   }
   stosb                { Pe o byte, AL, em ES:DI                   }
End;    { PutPixel }

Esta procedure  rpida o suficiente para comear, embora eu tenha dado uma
muito mais rpida uns tutoriais atrs que usa uma tcnica genial para pegar
DI.


 

OK, acho que  o suficiente para essa semana. Brinque com as rotinas de
PutPixel e veja o que voc pode fazer com elas. Para aqueles com um livro
do Peter Norton, veja que outros procedimentos voc pode fazer usando
interrupes.


 COISAS PARA FAZER:

    1) Cobrimos muita coisa nesse tutorial, e alguns conceitos importantes
       esto nele. Certifique-se de estar comfortvel com comparaes, porque
       vamos comear a testar bits em breve.

    2) Tenha certeza que entendeu aquela coisa de binrio -> decimal,
       decimal -> binrio, decimal -> hex e hex -> decimal. Faa voc mesmo
       alguns exemplos de soma e teste suas respostas com a calculadora do
       Windows.

    3) voc _deve_ entender shifts. Se voc ainda tem problemas, faa algumas
       expresses num papel e teste suas respostas num programa como:

       Begin   { Main }
          WriteLn(45 SHL 6);
          ReadLn;
       End.    { Main }

       e/ou a calculadora do Windows.

    4) D uma olhada na parte de VGA, e certifique-se de ter pego a teoria
       por trs disso, porque na prxima semana vamos entrar a fundo nisso.

Semana que vem vou tentar colocar alguns exemplos em C/C++ alm de Pascal 
para vocs programadores de C a fora.


 

No prximo tutorial vamos ver:

    Como a VGA  arrumada
    Como podemos desenhar linas e crculos
    Pegando e acertando a palette em Assembler
    Fades
    Alguns exemplos em C/C++

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

" Eu _nunca_ escrevo cdigo com bugs, eu apenas coloco algumas caractersticas
  a mais sem querer! "
