VGA – Tutorial 4 – Denthor

                       --==[ PARTE 4 ]==--
Autor: DENTHOR do ASPHYXIA
Atualizado por Snowman
Traduzido por Krull >>> tradução revisada e atualizada em 2019

Para obter os programas mencionados neste tutorial, por favor baixe o zip com a versão antiga do tutorial, neste link.

[Nota: essas coisas entre colchetes foram adicionadas pelo Snownan. O texto original ficou quase todo inalterado, exceto pela inclusão do material em C++]

Introdução

Olá pra todos! Bem-vindo à quarta parte dessa série de treinamento! Está um pouco atrasada, mas tenho certeza de que você vai achar que a espera valeu a pena, porque hoje eu vou lhe mostrar como usar uma ferramenta poderosa: telas virtuais.

Se você gostaria de me contactar, ou ao time, há muitos modos que você pode fazê-lo:
1) Escrever uma mensagem para Grant Smith em email privado aqui ou na Mailbox BBS
2) Escrever uma mensagem aqui na conferência de Programação, aqui na Mailbox (de preferência se você tem uma pergunta geral de programação ou problemas de que poderia interessar a outros)
3) Escrever para ASPHYXIA na ASPHYXIA BBS
4) Escrever para Denthor, Eze ou Livewire na Connectix.
5) Escrever para
Grant Smith
P.O.Box 270 Kloof
3640
6) ligar para mim (Grant Smith) no número 73 2129
(deixe uma mensagem se você ligar quando eu estiver na faculdade)

OBS1 : Se você é um representante de uma companhia ou BBS e quer que a ASPHYXIA faça um demo para você, mande um email pra mim; podemos discutir.
OBS2 : Se você fez/tentou fazer um demo, MANDE PARA MIM! Estamos nos sentindo muito solitários e queremos encontrar/ajudar/trocar código com outros grupos de demos. O que você tem a perder? Mande uma mensagem aqui e podemos ver como transferir. Nós realmente queremos ouvir de você.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

O que é uma tela virtual, e por que precisamos dela?

Digamos que você esteja gerando uma tela complexa várias vezes on-the-fly (por exemplo, fazendo um scroll da tela e então redesenhando todos os sprites para cada frame de um jogo que você está escrevendo). Você tem ideia de como seria esquisito se o usuário visse você apagando e redesenhando cada sprite para cada frame? Você consegue visualizar o efeito flicker (piscando) que isso resultaria? Você entende que haveria um efeito de “sprites dobrados” (quando você vê duas cópias do mesmo sprite um perto do outro)? No programa de amostra, eu incluí uma parte onde eu não uso telas virtuais, para demonstrar esses problemas, mas elas são, definitivamente o mais fácil de codificar.

Uma tela virtual é isso: uma seção de memória separada que é exatamente como a tela VGA em que você vai fazer todo o seu trabalho, e então jogar para a tela de verdade. Em EGA 640x350x16 você automaticamente tem uma página virtual, e é possível ter até 4 na MCGA usando um modo especial, mas para nossas propostas vamos setar uma, usando a memória base.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Setando uma tela virtual

Como você deve ter visto na primeira parte dessa série, a tela MCGA tem 64000 bytes (320×200=64000). Você pode ter notado também que em TP 6.0 [e C++] não é permitido tanto espaço para variáveis normais. Por exemplo, dizer:

[Pascal]
 VAR Virtual : Array [1..64000] of byte; 

[C++]
unsigned char Virtual[64000];

seria um “NÃO” em grande, já que você não teria mais espaço para suas outras variáveis. “Qual é a solução?” – ouço suas mentes gritando. A resposta: ponteiros! Ponteiros, para não usar os 64k alocados para você pelo seu TP 6.0 [e C++], eles pegam espaço de qualquer lugar da memória base de 640k de seu computador. Aqui está como você os setaria:

[Pascal]
Type Virtual = Array [1..64000] of byte;  { O tamanho de sua tela virtual  }
       VirtPtr = ^Virtual;                  
{ Ponteiro para a tela virtual   }
VAR Virscr : VirtPtr;                     
{ Nossa primeira tela Virtual    }
      Vaddr  : word;                        
{ Segmento de nossa tela virtual }

[C++] [Nota: Para simplificar, decidi manipular a alocação de memória de modo diferente na tradução para C++]
unsigned char *vaddr = NULL;             
// Ponteiro para a tela virtual // 

Se você colocar isso num programa, do jeito que está, e tentar acessar VirScr [ou vaddr], sua máquina provavelmente vai travar. Por quê? Porque você tem que pegar a memória para seus ponteiros antes de acessá-los! Faça desse jeito:

[Pascal]
Procedure SetUpVirtual;
  BEGIN
    GetMem (VirScr,64000);
    vaddr := seg (virscr^);
  END;

[C++]
void SetUpVirtual() {
    vaddr = (unsigned char *) calloc(64000,1);
  }

Esse procedimento pega a memória para a tela, e então seta o endereço vaddr para o segmento da tela. NUNCA DEIXE ESSE PROCEDIMENTO FORA DE SEU PROGRAMA! Se você deixar, quando escrever para suas telas virtuais você provavelmente estará escrevendo por cima do seu DOS ou algo desse tipo. Não é uma boa idéia 😉

Quando você tiver terminado seu programa, você vai querer liberar a memória que a tela virtual pegou, fazendo o seguinte:

[Pascal]
Procedure ShutDown;
  BEGIN
    FreeMem (VirScr,64000);
  END;

[C++]
void ShutDown() {
    free(vaddr);
  }

Se você não fizer isso, seus outros programas terão menos memória para usar.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Colocando um pixel na sua tela virtual

Isso é muito similar a colocar um pixel na sua tela MCGA comum, como discutido na parte um.. aqui está nossa putpixel original:

[Pascal]
Procedure PutPixel (X,Y : Integer; Col : Byte);
  BEGIN
    Mem [VGA:X+(Y*320)]:=col;
  END;

[C++]
void Putpixel (int x, int y, unsigned char Col) {
    memset(vga+(x+(y*320)),Col,1);
  }

Para nossa tela virtual, fazemos o seguinte:

[Pascal]
Procedure VirtPutPixel (X,Y : Integer; Col : Byte);
  BEGIN
    Mem [Vaddr:X+(Y*320)]:=col;
  END;

[C++]
void Putpixel (int x, int y, unsigned char Col) {
    memset(vaddr+(x+(y*320)),Col,1);
  }

Parece muito desperdício ter duas procedures fazendo exatamente a mesma coisa, apenas em duas telas diferentes, não é? Então, porque não combinamos as duas assim:

[Pascal]
Procedure PutPixel (X,Y : Integer; Col : Byte; Where : Word);
  BEGIN
    Mem [Where:X+(Y*320)]:=col;
  END;

[C++]
void Putpixel (int x, int y, unsigned char Col, unsigned char Where) {
    memset(Where+(x+(y320)),Col,1);
  }

Para usar isso, você diria algo desse tipo:

Putpixel (20,20,32,VGA);
PutPixel (30,30,64,Vaddr);

Essas duas frases desenham dois pixels… um na tela VGA e um na tela
virtual! Isso não faz você pular de alegria? 😉 Você deve ter notado
que ainda não podemos VER a tela virtual, na verdade, então vamos para
a próxima parte…

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Como jogar a tela virtual na tela de verdade

Vocês na verdade já têm as ferramentas para fazer isso sozinhos, a partir de informações de partes anteriores dessa série de treinamentos. Vamos, é claro, usar o comando Move [_fmemcpy], desse jeito:

[Pascal]
Move (Virscr^,mem [VGA:0],64000);

[C++]
_fmemcpy(vga,vaddr,64000);

Simples, né? Você pode querer esperar um retrace (Parte 2) antes de fazer isso, já que isso faria o “flip” mais suavemente (e, droga, mais lento).

Note que a maioria de nossas procedures podem ser alteradas para suportar a tela virtual, como a Cls, etc (veja a parte 1 dessa série), usando os métodos descritos acima (eu alterei a procedure CLS no programa de amostra que vai junto com essa Parte).

Nós, do ASPHYXIA, temos usado telas virtuais em quase todos os demos. Você pode imaginar como pareceria estranho o SoftelDemo se você tivesse que ver o fundo que se mexe se redesenhando, o texto e bolas vetoriais para CADA FRAME? O flicker, efeitos de sprites dobrados, etc, teriam deixado o demo horrível! Então, usamos uma memória virtual, e estamos muito satisfeitos com o resultado. Note, porém, que para conseguir a velocidade que precisávamos que nossa demo rodasse, escrevemos nossas rotinas de sprites, rotinas de flip, rotinas de pallette, etc, todas em assembly. O comando Move é muito rápido, mas não tão rápido como em ASM 😉

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Fechando

Estou escrevendo isso nos computadores da faculdade entre as aulas. Eu prefiro escrever e codificar entre 6 da tarde e 4 da manhã, mas não é uma boa idéia em época de aulas ;-), então essa é a primeira parte da série escrita antes de 9 da noite.

Me pediram para fazer uma parte sobre fazer scroll na tela, então isso é provavelmente o que eu vou fazer na semana que vem. Também, ASPHYXIA em breve vai colocar um pequeno demo com código fonte nos boards locais. Ele vai usar rotinas que temos discutido nessa série, e demonstrar quão poderosas essas rotinas são, se usadas da maneira correta.

Alguns projetos para você fazer:
1) Reescrever o comando de flip (o que joga a tela virtual na tela real), de modo que você possa dizer:
flip (Vaddr,VGA);
flip (VGA,Vaddr);
(É assim que o da ASPHYXIA funciona)
2) Colocar a maioria das rotinas (putpixel, cls, pal, etc) dentro
de uma unit, de modo que você não precise duplicar as
procedures em cada programa que você escrever. Se você precisar de ajuda, mande um email.

Vejo você na próxima semana
– Denthor
– Krull

A Nova Krull's HomePage