Então
você resolveu que vai mesmo desafiar os deuses da programação
e bulir com a mais mortal das ferramentas de programação:
o assembler. Também já decidiu
que irá fazê-lo de dentro da sua versão do
Delphi, quer os entendidos gostem ou não
dele. Ok, bem vindo ao mais fechado e restrito clube de criação
do mundo: aquele onde as pessoas realmente entendem o que estão
fazendo. Vamos lá...
Eu
disse anteriormente que os mnemômicos
indicam (ou tentam indicar) o que a instrução representa.
Como usaremos eles, não precisamos nos preocupar com seus
respectivos códigos numéricos, mas com o que eles
fazem. No menu de páginas temos um para uma tabela que
lista as principais instruções e o que elas fazem.
Uma
vez que tudo no mundo moderno da programação tem
a ver com visual, vamos começar por aí mesmo. Também
já vimos que uma das formas mais eficientes de lidar com
animações (e os jogos são basicamente animações)
é trabalhar com um buffer de conteúdo de tela que,
somente em determinados momentos, é enviado para o vídeo.
Podemos
então estabelecer o seguinte: o fundo do formulário
(form1) será nossa "tela de jogo"
e criaremos um TBitmap
de tamanho idêntico (seja ele qual for) para servir como
back buffer de tela. Ou seja, vamos plotar, pokear, escrever e
pintar o sete e o dezessete no TBitmap
e quando for o caso, transferimos o conteúdo para o form1.
Neste esquema, o "tamanho" da tela do jogo será
o tamanho que você, programador, der ao formulário
em tempo de desenvolvimento.
Primeiro,
no evento OnCreate
estabelecemos os parâmetros iniciais:
var Tela: TBitmap; Difr, Init: dword;
procedure TForm1.FormCreate(Sender: TObject);
begin
Tela:= TBitmap.Create;
Tela.Width:= ClientWidth;
Tela.Height:= ClientHeight;
Tela.PixelFormat:= pf32bit;
Tela.Canvas.FillRect(bounds(0,0,Tela.Width-1,
Tela.Height-1));
Init:= integer(Tela.ScanLine[0]);
Difr:= Init-integer(Tela.ScanLine[1]);
end; |
Definimos
Tela com 32 bits de profundidade de cor apenas
para que fique mais fácil trabalhar no assembler (os registradores
tem 32 bits). Criamos duas variáveis de 32 bits, para guardar
o endereço do início, na memória, da linha
0 da imagem. Uma vez que iremos atuar diretamente nos endereços,
temos que saber duas coisas: onde o TBitmap
é criado e qual a distância entre uma linha e outra,
em bytes.
Ora,
perguntaria o leitor mais atento: temos a largura da imagem para
nos dar essa "distância". Não é
bem assim, cara pálida. O sistema procura sempre organizar
as coisas de tal forma a facilitar as operações,
então, um número impar de pixels na largura pode
criar problemas para o processador lidar com endereços
quebrados. Fazemos o cálculo da forma como indiquei apenas
para nos certificarmos de que estamos mesmo usando as "distâncias"
corretas.
Mas, mas, mas... o cálculo deveria ser endereço
da linha 1 menos o endereço da linha 0. Não é
assim por uma simples razão: a imagem bitmap (bmp)
é colocada na memória do micro de cabeça
para baixo. Na verdade o conceito é um pouco mais sofisticado
do que isso: os endereços decrescem na medida em que avançamos
nas linhas. Não é bem a imagem que está de
cabeça para baixo, mas os endereços que são
tratados na ordem inversa.
Feito
isso, não devemos nos esquecer de arrumar a casa, quando
o ilustre usuário terminar o uso do seu programa:
procedure TForm1.FormDestroy(Sender: TObject); begin Tela.Free; end;
|
Para
fazer um refresh de imagem, ou seja, mandar o conteúdo
do buffer Tela para o form1, basta usar a função
Draw:
Vamos
agora desenhar uma linha horizontal, a partir da coordenada 0,0
(canto superior esquerdo) com 200 pixels de largura
e em vermelho para destacar do fundo branco. Coloque a programação,
por exemplo, no evento OnClick,
do form1:
procedure TForm1.FormClick(Sender: TObject); begin asm push ebx //preserva ebx mov ebx,Init //início da tela mov ecx,200 //tamanho da linha mov eax,00FF0000h //cor do pixel @Lp1: mov [ebx],eax //coloca o pixel add ebx,4 //salta 4 bytes loop @Lp1 //200 vezes até ecx = 0 pop ebx //restaura ebx end; Canvas.Draw(0,0,Tela); end;
|
Para
entender o que está acontecendo, vamos analisar linha a
linha:
Para
usar o assembler dentro do Delphi, temos que respeitar algumas
regras. Uma das mais importantes diz o seguinte:
An
asm statement must preserve the EDI, ESI, ESP, EBP, and EBX registers,
but can freely modify the EAX, ECX, and EDX registers.
Ou
seja, se pretendermos usar algum daqueles registradores indicados,
temos que preservar o seu conteúdo original. PUSH
e POP são instruções especiais
que colocam e retiram os respectivos conteúdos (valores)
numa pilha de dados chamada stack. Como se trata
de uma pilha (dados empilhados um em cima do outro) é preciso
tomar cuidado com a regra: o primeiro a entrar é o último
a sair.
mov ebx,Init //início da tela
|
Estamos colocando
o endereço inicial da tela (linha 0) no regristrador ebx,
pois é ele que usaremos como "apontador".
mov ecx,200 //tamanho da linha
|
O
registrador recebe o valor 200, porque faremos 200 vezes a mesma
operação (colocar os bytes/pixels uma a um no seu
devido lugar). Por que usei ecx? Simples: esse
registrado é uma espécie de contador automático
e é sempre usado em operações repetitivas.
mov eax,00FF0000h //cor do pixel
|
Essa
é a cor vermelha, já apresentada com as suas três
componentes RGB.
mov [ebx],eax //coloca o pixel
|
Aqui
estamos colocando um pixel na posição de memória
correspondente.
add ebx,4 //salta 4 bytes
|
Saltamos
4 bytes (endereços) porque cada pixel é formado
por essa quantidade de bytes (cor de 32 bits).
loop @Lp1 //200 vezes até ecx = 0
|
Trata-se
de uma operação automática que desvia a execução
do programa para o ponto indicado no label (@Lp1),
decrementando o regristrador ecx até que
o mesmo fique com o valor zero, quando o desvio não é
mais realizado.
Recupera
o valor original do registrador ebx e segue em
frente.
E
se quiséssemos fazer a linha vertical, com 200 pixels de
altura, partindo do ponto 0,0? Simples:
mov ebx,Init //início da tela mov ecx,200 //tamanho da linha mov eax,00FF0000h //cor do pixel @Lp2: mov [ebx],eax //coloca o pixel sub ebx,Difr //salta para a linha seguinte loop @Lp2 //200 vezes até ecx = 0
|
A
única diferença é que subtraímos o
valor da diferença, em bytes, entre as linhas, do registrador
ebx.
Mas
atenção criançada: ao contrário das
mordomias que tivemos até hoje, programando seguramente
em Delphi, quando passamos para o asm estamos de certa forma meio
órfãos de pai e mãe. Assim, se a nossa rotina
asm for plotar uma linha que esteja fora dos limites da tela,
vai dar um chabú legal. Na melhor das hipóteses
vai ocorrer um erro operacional e parar o programa. Na pior, o
micro trava e será necessário ressetá-lo.
A regra de ouro é: mantenha atenção redobrada
em tudo que fizer, ou então insira códigos e testes
na programação afim de evitar as situações
limites.
|
Download...
Clique no link para fazer
o download dos arquivos fonte tratados nessa página.
asm1.zip...
(160 Kb) |
Fontes
do project1. |
|
|