
                             VGA-Kurs - Part #7

Hallo Leute! Es wird Zeit fr 'T.C.P.'s Beginner's Guide To VGA Coding',
Part VII!
In den ersten 6 Teilen haben wir die Grundlagen des VGA Codings besprochen
(allerdings ohne dabei auf die Register der VGA-Karte selbst einzugehen, wer
dazu Informationen braucht, mge sich bitte ein Buch kaufen, ich empfehle
"VGA- und SVGA-Programmierung" von M. Uphoff, Addison Wesley).
Ich werde ab jetzt in den folgenden Teilen dieses Kurses diese Grundlagen als
Wissen vorraussetzen und auf verschiedene Effekte, Tricks oder hnliches
eingehen.
In diesem Teil will ich mit einem sehr simplen Effekt anfangen, mit dem sich
allerdings sehr schne Resultate erzielen lassen: Der Shadebob-Effekt.
Die Shadebobs sind meiner Meinung nach zu Unrecht wegen ihrer einfachen
Programmierung in Verruf gekommen, und werden hufig als Lamer-Effekt betitelt.
Ich fr meinen Teil habe nichts gegen Shadebobs in Demos, auch wenn sie schon
etwas lter sind. Man kann mit dem Prinzip wirklich sehr viel anfangen, man
sollte es nur nicht bertreiben, wie es manche ltere Demos zeigen, in denen
zum Teil bis zu 5 Minuten lange Shadebob-Parts enthalten sind.
Aber fangen wir jetzt an.
Das Prinzip der Shadebobs ist einfach erklrt. Wenn man sich ein beliebiges
Quadrat auf dem schwarzen Screen sucht, und alle Pixelwerte dieses Quadrates um
eins erhht, sieht man ein blaues Quadrat, weil in der Standardpalette nach der
Farbe 0 (schwarz) die Farbe blau kommt. Wenn man nun ein anderes, gleichgroes
Quadrat nimmt, das sich mit dem alten in einem Pixel berlagert, dann ist dieses
auch blau, nur das eine Pixel, an dem sich die beiden berlagern, hat die Farbe
grn, denn sein Farbwert wurde zweimal erhht.
Falls ihr euch nicht so genau vorstellen knnt, wie das vor sich geht, hilft
euch vielleicht das Listing weiter.

uses crt;
var n1,n2 : byte;
begin
  asm mov ax,13h; int 10h end;
  for n1 := 0 to 9 do
    for n2 := 0 to 9 do mem[$A000:n2*320+n1] := mem[$A000:n2*320+n1] + 1;
  for n1 := 9 to 18 do
    for n2 := 9 to 18 do mem[$A000:n2*320+n1] := mem[$A000:n2*320+n1] + 1;
  readkey;
  asm mov ax,3; int 10h end;
end.

Wenn wir jetzt eine Farbpalette setzen, die einen durchgehenden blauen
Farbverlauf enthlt, und stndig neue Quadrate an zuflligen Positionen auf den
Bildschirm setzen, htten wir den ersten Shadebob-Typ: Die Colorcycle-Bobs.

uses crt;
var Pal : array[0..767] of byte;
    n1,n2 : byte;

procedure SetPalette;assembler;    { Setzt die Palette in Pal }
asm
  mov     dx,3C8h
  xor     al,al
  out     dx,al
  mov     cx,768
  mov     dx,3C9h
  mov     si,offset pal
@Jmp1:
  lodsb
  out     dx,al
  loop    @Jmp1
end;

procedure BluePal;   { Schreibt eine blaue Palette in Pal setzt sie mittels }
var loop : integer;  { SetPalette }

begin
  for loop := 0 to 31 do begin
    pal[loop*3+2] := loop * 2;
    pal[(63-loop)*3+2] := loop * 2;
    pal[(loop+64)*3+2] := loop * 2;
    pal[(127-loop)*3+2] := loop * 2;
    pal[(loop+128)*3+2] := loop * 2;
    pal[(191-loop)*3+2] := loop * 2;
    pal[(loop+192)*3+2] := loop * 2;
    pal[(255-loop)*3+2] := loop * 2;
  end;
  setpalette;
end;

procedure SetBob(x,y:word);  { Setzt ein Bob an die Koords X,Y }
var n1,n2 : byte;

begin
  for n1 := 0 to 19 do    { Hier werden in einem 20x20 Pixel groen Quadrat }
    for n2 := 0 to 19 do  { alle Pixelwerte um 1 erhht }
      mem[$A000:(n2+y)*320+n1+x] := mem[$A000:(n2+y)*320+n1+x] + 1;
end;

begin
  asm mov ax,13h; int 10h end;
  BluePal;
  randomize;
  repeat
    SetBob(random(300),random(180)); { Bob an zufllige Position setzen }
  until keypressed;
  readkey;
  asm mov ax,3; int 10h end;
end.

Das obige Listing enthlt noch eine Pascal-Version der SetBob-Routine, die
nicht gerade sehr optimiert ist. Wer also gerade zu viel Zeit hat, kann das
Ganze noch ein bichen optimieren.
In der dritten Zeile der SetBob-Prozedur kann das '+ 1' durch ein '+ random(4)'
ersetzt werden. Dadurch wird der Effekt krniger. Ist Geschmackssache.
So, das Prinzip der Shadebobs drfte jetzt klar sein.
Als zweite in der Reihe der verschiedenen Shadebob-Typen wren da die Snakebobs.
Bei dieser Art von Bobs werden entweder ein oder mehrere (je mehr desto besser)
Shadebobs gezeichnet und dann z.B. um ein Pixel nach rechts und eins nach unten
bewegt. Das macht man solange, bis der Bob am unteren Rand des Screens angelangt
hat. Man hat jetzt eine Line, die schrg ber den Bildschirm verluft. Nun
ndert man die Bewegungsrichtung, indem man den Inkrementationsparameter
umkehrt, also den Bob jetzt nach oben bewegt. Trifft der Bob nun auf den rechten
Rand des Screens, kehrt man auch die horizontale Bewegungsrichtung um.
Das sieht dann so aus:

uses crt;
var Pal : array[0..767] of byte;
    x1,y1,x2,y2 : word;            { X- und Y-Koords der beiden Bobs }
    incx1,incy1,incx2,incy2 : shortint; { Inkrementationsparameter }

{ Hier die Prozeduren SetBob und SetPalette einfgen }

procedure RainbowPal; { Erstellt eine bunte Palette }
var loop : integer;

begin
  for loop := 0 to 31 do begin
    pal[loop*3] := loop * 2;
    pal[(63-loop)*3] := loop * 2;
    pal[(loop+64)*3+1] := loop * 2;
    pal[(127-loop)*3+1] := loop * 2;
    pal[(loop+128)*3+2] := loop * 2;
    pal[(191-loop)*3+2] := loop * 2;
    pal[(loop+192)*3] := loop * 2;
    pal[(loop+192)*3+1] := loop * 2;
    pal[(loop+192)*3+2] := loop * 2;
    pal[(255-loop)*3] := loop * 2;
    pal[(255-loop)*3+1] := loop * 2;
    pal[(255-loop)*3+2] := loop * 2;
  end;
  setpalette;
end;

begin
  asm mov ax,13h; int 10h end;
  RainbowPal;
  randomize;
  x1 := random(280);               { Zufllige X- und Y- Positionen fr }
  y1 := random(160);               { beide Bobs }
  x2 := random(280);
  y2 := random(160);
  incx1 := 1;                      { Bob 1 wird nach rechts und nach unten }
  incy1 := 1;                      { bewegt }
  incx2 := -1;                     { Bob 2 wird nach links und nach unten }
  incy2 := 1;                      { bewegt }
  repeat
    inc(x1,incx1);                 { Bob-Positionen verndern }
    inc(y1,incy1);
    inc(x2,incx2);
    inc(y2,incy2);
    if (x1 = 299) or (x1 = 0) then incx1 := -incx1; { berprfen, ob einer }
    if (y1 = 179) or (y1 = 0) then incy1 := -incy1; { der Bobs am Rand des }
    if (x2 = 299) or (x2 = 0) then incx2 := -incx2; { Screens ist, und wenn }
    if (y2 = 179) or (y2 = 0) then incy2 := -incy2; { ja dann Wert umkehren }
    SetBob(x1,y1);                 { Die beiden Bobs zeichnen }
    SetBob(x2,y2);
  until keypressed;
  readkey;
  asm mov ax,3; int 10h end;
end.

Wir kommen nun zu meinen persnlichen Lieblingsbobs, den Sinusbobs.
Der Name basiert darauf, da hier mittels einer Sinusformel die Koordinaten
fr die Bobs berechnet werden, wodurch sehr schne Formen zustande kommen.
Allerdings sind die Quadratischen Bobs hier nicht geeignet, passender sind
runde. Man kann stattdessen natrlich auch kleine Symbole oder Logos benutzen,
das sieht nicht schlecht aus.

uses crt;

const VGA : word = $A000;
      SinOfs = 40;        { Offset                     }
      SinAmp = 50;        { Amplitude                  }
      SinLen = 255;       { und Lnge der Sinustabelle }
      SprPic : array[0..15,0..15] of byte = (
        (0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,0),
        (0,0,0,0,2,2,3,3,3,3,2,2,0,0,0,0),
        (0,0,0,2,3,3,3,3,3,3,3,3,2,0,0,0),
        (0,0,2,3,3,3,3,3,3,3,3,3,3,2,0,0),
        (0,2,3,3,3,3,3,3,3,3,3,3,3,3,2,0),
        (0,2,3,3,3,3,3,3,3,3,3,3,3,3,2,0),
        (2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2),
        (2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2),
        (2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2),
        (2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2),
        (0,2,3,3,3,3,3,3,3,3,3,3,3,3,2,0),
        (0,2,3,3,3,3,3,3,3,3,3,3,3,3,2,0),
        (0,0,2,3,3,3,3,3,3,3,3,3,3,2,0,0),
        (0,0,0,2,3,3,3,3,3,3,3,3,2,0,0,0),
        (0,0,0,0,2,2,3,3,3,3,2,2,0,0,0,0),
        (0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,0)
      );

var SinTab : array[0..SinLen] of word;
    Pal : array[0..767] of byte;
    X,Y,n       : integer;
    I1,I2,J1,J2 : byte;

{ Hier die Prozeduren SetPalette und BluePal einsetzen }

procedure CalcSinus(SinPar:byte);
begin
  for n := 0 to SinLen do
    SinTab[n] := round(sin(n*SinPar*pi/SinLen)*SinAmp)+SinOfs;
end;

procedure WaitRetrace;assembler;
asm
  mov     dx,3DAh
@loop1:
  in      al,dx
  and     al,08h
  jz      @loop1
@loop2:
  in      al,dx
  and     al,08h
  jz      @loop2
end;

procedure SetBob(X,Y:word;W,H:byte;Sprite:pointer);assembler;
asm
  push    ds                       { DS sichern }
  lds     si,[Sprite]              { DS:SI mit dem Spritepointer laden }
  mov     es,vga                   { VGA-Segment nach ES }
  cld
  mov     ax,Y                     { Offset des Bobs berechnen }
  shl     ax,6
  mov     di,ax
  shl     ax,2
  add     di,ax
  add     di,X
  mov     bh,H
  mov     cx,320
  sub     cl,W
  sbb     ch,0
@L:
  mov     bl,W
@L2:
  lodsb                            { Wert laden }
  or      al,al                    { Wert = 0 ? }
  jz      @S                       { Wenn ja, nicht erhhen }
  mov     dl,es:[di]               { Pixelwert vom VGA holen }
  add     dl,al                    { Wert erhhen }
  and     dl,63
  mov     es:[di],dl               { und neuen Pixelwert schreiben }
@S:
  inc     di                       { nchste Pixelposition }
  dec     bl                       { Zhler dekrementieren }
  jnz     @L2                      { wenn <> 0 dann innerer Loop }
  add     di,cx                    { nchste Zeile auf VGA }
  dec     bh                       { Zhler dekrementieren }
  jnz     @L                       { wenn <> 0 dann uerer Loop }
  pop     ds
end;

begin
  asm mov ax,13h; int 10h end;
  BluePal;
  randomize;
  CalcSinus(random(8));  { Sinustabelle berechnen }
  I1 := 0;               { Indizes fr Sinustabelle }
  I2 := 200;
  J1 := 0;
  J2 := 200;
  repeat
    X := SinTab[I1]+SinTab[I2];  { Werte addieren }
    Y := SinTab[J1]+SinTab[J2];
    inc(I1,2);                   { Neue Indexwerte }
    inc(I2,3);
    inc(J1);
    inc(J2,2);
    waitretrace;
    SetBob(80+X,Y,16,16,addr(SprPic)); { Bob zeichnen }
  until keypressed;
  readkey;
  asm mov ax,3; int 10h end;
end.

Das Wesentliche hier, das Berechnen der Sinustabelle, findet in der Prozedur
CalcSinus statt, der ein Parameter bergeben wird, nach dem sich die sptere
Form der Bobs richtet. Ebenso knnen die Konstanten fr das Offset und die
Amplitude der Tabelle am Anfang des Listings verndert werden. Wer ein bichen
Ahnung von Mathe hat, kann so zu sehr schnen Formen kommen.
Die Prozedur SetBob ist in diesem Fall 100% Assembler, allerdings zeichnet sie
kein Quadrat, sondern das Sprite SprPic auf den Bildschirm, wobei sie die
Pixelwerte um die Werte im Sprite erhht.

Ich hoffe, ihr knnt mit diesen schnen Effekten etwas anfangen und aus ihnen
lernen. Es gibt noch eine Menge anderer Mglichkeiten, die einem die Shadebobs
bieten. Man knnte sie zum Beispiel mit ein paar Vektorroutinen verbinden.
Auch Delay-Vektoren wren hiermit mglich, allerdings ist es wohl sinnvoller,
wenn man, anstatt alle Farbwerte des vorigen Objekts herabzusetzen, das Objekt
neu in einer dunkleren Farbe zeichnet.
In der nchsten Ausgabe werde ich wahrscheinlich einen anderen Effekt
beschreiben. Welchen, wei ich noch nicht genau.





[ This text copyright (c) 1995-96 Johannes Spohr. All rights reserved. ]
[ Distributed exclusively through PC-Heimwerker, Verlag Thomas Eberle. ]
[                                                                      ]
[ No  part   of  this   document  may  be   reproduced,   transmitted, ]
[ transcribed,  stored in a  retrieval system,  or translated into any ]
[ human or computer language, in any form or by any means; electronic, ]
[ mechanical,  magnetic,  optical,   chemical,  manual  or  otherwise, ]
[ without the expressed written permission of the author.              ]
[                                                                      ]
[ The information  contained in this text  is believed  to be correct. ]
[ The text is subject to change  without notice and does not represent ]
[ a commitment on the part of the author.                              ]
[ The author does not make a  warranty of any kind with regard to this ]
[ material, including,  but not limited to,  the implied warranties of ]
[ merchantability  and fitness  for a particular  purpose.  The author ]
[ shall not be liable for errors contained herein or for incidental or ]
[ consequential damages in connection with the furnishing, performance ]
[ or use of this material.                                             ]
