ASM – Tutorial 8 – Adam Hyde

Tutorial de Assembler de Adam Hyde 1.0

PARTE 8

Traduzido por Renato Nunes Bastos

 

Versão:  1.2
Data:  28-06-1996 / online by Renato 03-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

 


Estruturas de Dados em Assembler | Referenciando Estruturas de Dados em Assembler | Criando
Arrays em Assembler
| Indexando Arrays em Assembler | Operadores Lógicos | O Programa Demo


Bem, bem-vindo de volta programadores de Assembler.  Este tutorial está realmente atrasado, e teria chegado muito mais tarde se não fosse por Bjorn Svensson, e muitos outros como ele, que graças à sua determinação 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 nós vamos realmente aprender algo.  Vamos dar uma olhada mais de perto em como podemos declarar variáveis, e aprofundar no mundo de estruturas.  Você aprenderá a criar arrays em Assembler, e este conceito é reforçado 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 variáveis. Porém, até agora nós os temos usado como você usaria a declaração de Const em Pascal. Quer dizer, temos usado isto para atribuir um valor a um Byte ou a uma Word.

Ex.:

MyByte DB 10  —  que é o mesmo que  —  Const MyByte : Byte = 10;

Contudo, poderíamos dizer:

MyByte DB ?

…e então dizer depois:

MOV MyByte, 10

De fato DB realmente é muito poderoso. Há vários tutoriais atrás, 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 vocês provavelmente teria dito a si próprio: “Peraí!…  aquele cara do tutorial disse que DB declara um BYTE.  Como é que o DB pode declarar uma string, então “?  Bem, DB tem a habilidade de reservar espaço  para valores de vários bytes – de 1 a tantos bytes quanto você precisa.

Você também pode ter desejado saber o que os números 10 e 13 antes do texto representavam. Bem, dê uma olhada na sua tabela ASCII e veja o que são 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, então você viu como criar variáveis corretamente. Mas, e constantes?  Bem,
em Assembler, constantes são conhecidas como Equates. Equates fazem a  codificação
em Assembler muito mais fácil, 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ê não pode mudar isto.
  • EQU pode ser usado para definir quase qualquer tipo – inclusive strings. Contudo, você não pode fazer isto quando se usa um ‘ = ‘.  Um ‘ = ‘ só podedefinir valores numéricos.
  • 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 cheio de truques na codificação em Assembly – estruturas. Estruturas não são variáveis, são um TIPO – basicamente um esquema de uma variável.

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

Porém, 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 código.

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 são os seguintes:

  • Você pode declarar uma estrutura em qualquer lugar em seu código, 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 não reserva qualquer byte de memória para a mesma. Isso só acontece quando você declara uma variável dessa estrutura – só nesse momento a memória é alocada.

 

REFERENCIANDO ESTRUTURAS DE DADOS EM ASSEMBLER

 

Bem, você viu como definir estruturas, mas como você se refere de verdade a elas em seu código?

Tudo o que você tem a fazer, é colocar em algum lugar algumas linhas como as seguintes em seu programa – de preferência 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 três campos preenchidos. Dia é setado para 19, Mês para 6 e Ano para 1996. Agora, o que esses símbolos,”< >”, estão fazendo depois de data? – você pergunta.

Os parênteses nos apresentam um outro modo de alterar os conteúdos dos campos da variável.  Se eu tivesse escrito isto:

Date_I_Passed_Physics   Date <10,10,1900>

…então os campos teriam sido mudados para os valores nos parênteses.
Alternativamente, teria sido possível fazer isto:

Date_I_Passed_Physics   Date <,10,>

E só agora o campo de Mês foi mudado.  Note que neste exemplo, a segunda vírgula não era necessária, pois nós não mudamos outros campos posteriores. É sua escolha, (e do compilador!), se deixar a segunda vírgula ou não.

Agora tudo isso tá indo muito bem, mas como você se estes valores em seu código? 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 são bem fáceis 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 Variável, tem a seguinte sintaxe:

 <rótulo>    <diretiva> <contador> DUP  (expressão)

Onde (expressão) é 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, então você poderia dizer
isto:

MyArray DW 20 DUP (0)

E, como outro exemplo de como o Assembler é flexível, 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, então você poderia fazer
isto:

  MOV BL, [OutroArray + 23]; Ou, seria possível dizer:
  MOV AX, 23,
  MOV BL, [OutroArray + AX]

NOTA:  Não esqueça que todos os arrays começam no elemento ZERO. Linguagens de alto-nível como C e Pascal fazem você esquecer isto devido ao modo que eles deixam você referenciar arrays.


Agora, isso foi fácil, mas, e se OutroArray fosse de 50 WORDS, não BYTES?

OutroArray DW 50 DUP (?)   ; como esse.

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

  MOV AX, 23 ; Acesse o elemento 24
  SHL AX, 1        ; Multiplique AX por dois
  MOV BX, [OutroArray + AX] ; Bote o elemento 24 em BX

Não é tão difícil assim, né? Porém, este método pode ser complicado quando você não tem cálculos fáceis para fazer quando o índice não é uma potência de dois.

Digamos que você tivesse um array que tem um tamanho de elemento de 5 bytes. Se nós quiséssemos conferir o sétimo elemento, nós teríamos que fazer algo assim:

  MOV AX, 6 ; Pega o sétimo 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

Porém, como eu disse antes, MUL não é um modo muito eficiente de codificação, 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 números de ponto flutuante. Agora, números de ponto flutuantes podem ser desajeitados para se manipular em Assembler, assim vê se não sai escrevendo aquele programa de planilha eletrônica que você sempre quis, em código de máquina! Porém, quando estiver trabalhando com mapeamento de textura, círculos e outras funções mais complicadas, é inevitável que você precise de algo para declarar números de ponto flutuante.

Digamos que quiséssemos armazenar Pi. Para declarar Pi, nós precisamos usar a diretiva DT. Você poderia declarar Pi assim:

Pi DT 3.14

DT na verdade reserva dez bytes de memória, assim seria possível declarar Pi com um número maior de casas decimais.

Eu não vou entrar nas particularidades de números de ponto flutuante neste tutorial. Quando nós precisarmos deles mais tarde, eu falo sobre isso.


Certo, no último tutorial disse eu que eu daria algum tipo de resumo do que nós cobrimos durante os últimos quatro meses.  (Ei – isso é como se fosse um tutorial a cada duas semanas, então talvez eles não tenham saído tão irregularmente, afinal de contas!)

De qualquer maneira, eu vou falar sobre a parte de pegar e setar bits individuais num registrador, porque este é um tópico importante que eu deveria ter coberto há muito tempo atrás.


 

OPERADORES LÓGICOS

 

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

(A propósito, em uma edição de Tutorial Cinco, eu errei a tabela para XOR, amavelmente apontado por Keith Weatherby, assim se você não tem a versão mais atual, V 1.3), então 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 edições 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. Nós podemos usar o AND para mascarar bits em  um registrador ou variável, 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 próprios fins. Um uso bom para AND seria conferir se um caracter lido do teclado é uma maiúscula ou não.  (Você pode fazer isto, porque a diferença entre uma maiúscula e sua minúscula é de um bit.

Ex:
   'A' =  65   = 01000001
   'a' =  97   = 01100001
   'S' =  83   = 01010011
   's' =  115  = 01110011

Assim, da mesma forma que você pode fazer um AND de números binários, você poderia usar uma aproximação semelhante para escrever uma rotina que confere se um caracter é
maiúsculo ou minúsculo.

Ex: 
    0101 0011                     0111 0011
AND 0010 0000                 AND 0010 0000   
  = 0000 0000                   = 0010 0000
^^^ Essa é maiúscula ^^^      ^^^ Essa é minúscula ^^^

Agora, e o OR? O OR é geralmente usado depois de um AND, mas não tem que ser. Você pode usar OR para mudar bits individuais em um registrador ou variável sem mudar quaisquer um dos outros bits. Você poderia usar OR para escrever uma rotina para mudar  um caracter para maiúsculo se já não for, ou talvez para minúscula se fosse maiúscula.

Ex:
    0101 0011
 OR 0010 0000
=   0111 0011
^^^ S maiúsculo agora foi mudado para s minúsculo ^^^

A combinação de AND/OR é um dos truques mais frequentemente usados no mundo do Assembly, assim tenha certeza de que você entendeu bem o conceito. Você me verá frequentemente usando-os, tirando proveito da velocidade das instruções. Finalmente, e o XOR?  Bem, o OU exclusivo pode ser às vezes muito útil. XOR pode ser de útil para alternar bits individuais entre 0 e 1 sem ter que saber qual o conteúdo que cada bit tinha anteriormente. Lembre-se, como com OU, uma máscara de zero permite ao bit original continuar com seu valor.

Ex:
    1010 0010
XOR 1110 1011 
  = 0100 1001

Faça alguma tentativa para aprender estes operadores binários, e o que eles fazem. Eles são uma ferramenta inestimável quando se está trabalhando com números binários.OBS.:  Para simplicidade, o Turbo Assembler lhe permite usar números binários em seu código. Por exemplo, seria possível 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 PROGRAMA DEMONSTRATIVO

Certo, chega da parte chata – vamos ao programa demonstrativo que eu incluí! Eu pensei que já era sem tempo escrever outra demonstração – 100% Assembler desta vez, e vamos a uma rotina de fogo. Rotinas de fogo podem parecer bem  efetivas, e são surpreendentemente fáceis de se fazer, assim, pensei, por que não…Agora, os princípios de uma rotina de fogo são 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 rápido seu programa será, e quanto maior você o fizer, o mais bem definido o fogo será. Você precisa acertar um equilíbrio 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 acabei comprometendo a velocidade. A resolução horizontal é boa – 1 pixel por elemento de array, mas a resolução 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. É rápido, mas com baixíssima definição.

  • Faça uma palette agradável

Seria idéia 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 nós vamos usar as chamas que nós conhecemos agora.  🙂

Agora o loop principal começa. No loop você deve:

  • Criar uma linha de fundo ramdômica, 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á chegando lá.

  • Suavize o array:

Agora este é a única parte 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 média.
  • Tire um da média.
  • Ponha a média – 1 no array DIRETAMENTE ACIMA onde o pixel velho estava. (Você pode alterar isto, e digamos, pôr acima e à direita, e então parecerá que a chama está sendo soprada pelo vento.)
  • Faça isso até você chegar à última linha.

 

  • Copie o array para a tela

Se seu array é de 320 x 200, então você pode copiar elemento-para-pixel.  Se não é, então coisas são mais difíceis. 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 então 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 não de 320 x 100.  Bem, a razão para isto é bastante simples. Se eu tivesse usado 320 x 100 como minhas dimensões de array, e então copiasse isso para a tela, as últimas quatro linhas teriam parecido bem estranhas.  Elas não teriam sido suavizados corretamente, e o resultado final não estaria de todo flamejante. Assim, eu apenas copiei até a linha 100 para a tela, e deixei o resto pra lá.

Como experiência, tente mudar a terceira linha abaixo no procedimento de DrawScreen para   MOV BX, BufferY  e mudar as dimensões para 320×100 e veja o que acontece.

MOV   SI, OFFSET Buffer ; Aponta SI para o início do buffer
XOR   DI, DI        ; Começa a desenhar em 0, 0MOV   BX, BufferY - 4
                    ; Perde as 4 últimas linhas do
                    ; buffer. Estas linhas não vão se parecer
                    ; com fogo de jeito nenhum.
  • Volta para o início.

Bem, não importa o quão bem eu expliquei isso tudo, é muito difícil de ver o que está acontecendo sem olhar o código. Então agora nós 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 código < 64K
.STACK 200H    ; Arruma 512 bytes de espaço para a pilha
.386 

Aqui, eu disse que o programa terá um segmento de código e de dados total de menos que 128K. Eu vou dar para o programa uma pilha de 512 bytes, e permitir instruções do 386.

.DATA
 CR EQU 13
 LF EQU 10

O segmento de dados começa, e eu dou para CR e para LF os valores de “carriage return” e “line feed” (retorno de carro e alimentação de linha, i.e, volta pro início 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 metade do meu número de
                  ; telefone - não 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 número que é necessário para começar o procedimento Random, e pode ser qualquer coisa que você quiser.

Eu também economizei algum espaço e pus os dados para a palette em um arquivo externo que é incluído no código assembly. Dê uma olhada no arquivo. Usar INCLUDE pode economizar muito espaço e confusão.Eu pulei alguns procedimentos que são bastante auto-explicativos, e fui direto para a procedure DrawScreen.

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

Isto também é fácil 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       ; Começa 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          ; Não, então 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 não chega tão alto.
Experimente… seja criativo!  🙂

NextPixel:
  MOV   DS:[SI-BufferX], AL   ; Põe o novo valor no array
  INC   SI         ; Próximo pixel
  DEC   CX         ; Um a menos para fazer
  JNZ   Alter      ; Já fizemos todos?
  RETAveragePixels 
ENDP

Agora nós vimos a procedure que faz toda a suavização. Basicamente, nós 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 média. A média é então plotada diretamente sobre o pixel atual.

Para mais informação relativo à instrução 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     ^^^ Este programa 
     retorna 256         retorna 0    

Lembre-se de que ADC é usado para ter certeza que quando um registrador ou variável não é grande bastante para armazenar um resultado, o resultado não 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. 

Nós apontamos DS primeiramente para o segmento de dados, de modo que possamos ter acesso a todas nossas variáveis.

CALL  InitializeMCGA
CALL  SetUpPalette

MainLoop:
  CALL  AveragePixels
  MOV   SI, OFFSET Buffer + BufferX * BufferY - BufferX 
  SHL 1; SI agora aponta para o início da segunda última linha (?????? - by Krull)
  MOV   CX, BufferX SHL 1   ; Prepara para pegar BufferX x 2 números randômicos

BottomLine:
  CALL   Random        ; Pega um número randômico
  MOV    DS:[SI], DL   ; Usa apenas o byte baixo de DX, i.e.,
  INC    SI            ; o número 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 graças ao autor desconhecido da USENET – retorna um valor muito alto em DX:AX. Porém, nós só requeremos um número de 0 a 255, assim, usando só DL, nós temos tal número.

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      ; Não, segue em frente
MOV   AH, 00H       ; Sim, então 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 também é bem fácil 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 idéia de como uma rotina de fogo funciona.

De qualquer maneira, a meta era não lhe ensinar como fazer uma rotina de fogo, mas como usar arrays, assim se você pegou o negócio do fogo também, então isso é um bônus. 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ê não entendeu como se usa arrays com isso tudo, então talvez você nunca entenda, pelo menos não com meu tutorials, sem dúvida.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 tópico discutido em um tutorial futuro, então me escreva, e eu verei o que eu posso fazer.Não perca!!!

Pegue o tutorial da semana que vem da minha homepage em:

Até semana que vem!

– Adam.
– Krull.

3 thoughts on “ASM – Tutorial 8 – Adam Hyde”

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

 

A Nova Krull's HomePage