
                             VGA-Kurs - Part #2

Moin! Hier, nun und jetzt, wie zu erwarten "T.C.P.s' Beginner's Guide To VGA
Coding"(TM), Part II!
Wie schon im ersten Teil gesagt, werde ich diese Serie nur fortsetzen, wenn 
sich auch genug Leute melden, mich mit Lob besudeln, mit Klagen zuballern
oder mir einfach ein paar Tips geben.
Na dann, aufi gaits Buam!
Heute wollen wir uns einem wichtigen und fr Anfnger oft sehr verwirrenden
Kapitel der VGA-Programmierung zuwenden: Der VGA-Palette!
Aber erst dies: Ihr erinnert euch doch bestimmt an unsere "optimierte"
PutPixel-Routine aus der ersten Ausgabe; Sie schrieb die Pixelwerte direkt
in den Bildschirmspeicher und war so bedeutend schneller als die
Bios-Variante. Wollen mal sehen, ob wir nicht noch ein paar Clocks
rausquetschen knnen:

procedure PutPixel(x,y:integer;
                 col:byte);assembler;
asm
  mov     ax,0A000h
  mov     es,ax
  mov     bx,[x]
  mov     dx,[y]
  mov     di,bx
  mov     bx,dx
  shl     dx,8
  shl     bx,6
  add     dx,bx
  add     di,dx
  mov     al,[col]
  stosb                          
end;

(Hinweis: Fr diese Prozedur mt ihr entweder im Compiler-Optionsmen die 
286er-Code Erzeugung aktivieren (oder am Anfang des Programms den
Compiler-Schalter {$G+} setzen) oder statt "shl dx,8" 8 mal "shl dx" und 
statt "shl bx,6" 6 mal "shl bx" schreiben.)
Um den Sinn dieser Prozedur auf Anhieb zu verstehen, mu man schon etwas mehr
Erfahrung haben: Als erstes wird (wie wahrscheinlich noch alle erkannt haben)
die Adresse des VGA-Segments nach ES geMOVt (Manche werden wissen, da das
Register-Paar ES:DI einen Zeiger darstellt, und nun in DI das Offset des
gewnschten Pixels stehen mu). Anschlieend kommt die Adresse von x bzw. y
nach BX bzw. DX. Nun wird (nachdem x nach BX nach DI und DX nach BX verschoben
wurde, der Inhalt von DX (y) nun also in BX und DX ist) der Wert in DX um 8
(entspricht einer Multiplikation mit 256) und der in BX um 6 (entspricht
Mult. mit 64) nach links geshiftet. Dann werden die beiden addiert, und was
haben wir jetzt? Y mult. mit 320! (Teuflisch, nicht?) Dazu wird nun der Wert
von X addiert, dann die Farbe nach AL gebracht und STOSB aufgerufen. Schon
haben wir den Pixel dort, wo wir ihn haben wollten!
So, nun zu den Paletten. Wie wohl jeder wei, hat man im Modus 13h 256 Farben
zur Verfgung. Auswhlen kann man diese 256 Farben aber aus einem Bereich von
262144 verschiedenen Farbtnen. Nun, die Standard-Auswahl ("Palette") der
VGA-Farben ist nicht sehr klug gewhlt und oft findet man die Farbe, die man
braucht, nicht in dieser Auswahl. Also, wie beschafft man sich neue?
Vorerst einmal ein paar Grundlagen (Ich wei, Theorie ist Bullshit, aber sie
mu trotzdem sein). Ihr habt bestimmt in der Schule mal gelernt, da Farbtne
aus den drei Grundfarben entstehen: Rot, Grn (eigentlich Gelb) und Blau
(Wenn nicht, wit ihr es sptestens jetzt). Dadurch lassen sich theoretisch
endlos viele Farben herstellen (die aber eigentlich niemand braucht ;-),
indem man ebendiese Farben zu bestimmten Anteilen miteinander vermischt.
Aber was hat das jetzt mit der VGA-Palette zu tun? Ganz einfach, sie basiert
auf demselben Prinzip! Wenn man wei wie, kann man sich ber 260000 neue
Farbtne erstellen! Aber wie? Jede unserer 256 Standard-VGA-Farben besteht
schon aus diesen 3 Farbwerten. Die Farbe 0 z.B. ist schwarz und besteht somit
aus den Werten (0,0,0), also: 0 Rot, 0 Grn, 0 Blau. Farbe 31 dagegen (wei)
hat die Werte (63,63,63), also die Maximalwerte jedes Farbtons. So sind nun
smtliche Farben aufgebaut. Wenn man ein tiefes Blau hat (0,0,63), kann man
es aufhellen, indem man die Werte fr Rot und Grn heraufsetzt (32,32,63).
Erhht man dagegen nur den Wert fr Rot, erhlt man ein dunkleres Blau bis
Lila.
So, nun zum praktischen Teil: Wie setze ich die Palettenwerte einer Farbe?

procedure SetPal(col,R,G,B:byte);
begin
  port[$3C8] := col;
  port[$3C9] := R;
  port[$3C9] := G;
  port[$3C9] := B;
end;

Hier wird erst dem DAC-Port(3C8h) die Nummer der Farbe zugewiesen, um ihn auf 
einen Schreibzugriff vorzubereiten. Anschlieend werden die 3 Farbwerte an
den Port 3C9h abgeschickt, und -zack!- sitzen die neuen Werte in der Palette.
Dies ist sehr ntzlich, weil alle Farben der Nummer col auf einen Schlag eine
neue Farbe bekommen, was man trickreich ausnutzen kann. Ein Beistiel: Die
Palettenmanipulation wird oft in Adventures eingesetzt, wenn in einer Location
ein Wasserfall simuliert werden soll. Dieser wird erst gezeichnet, und
anschlieend werden die Werte der Farben des Wasserfalls (meist Blautne)
stndig miteinander verstauscht, so da es aussieht, als wrde wirklich Wasser
flieen. Aufwendige Animationen, die Speicherplatz verschwenden, bleiben einem
erspart (Diese Methode nennt sich brigens Colorcycling). Es gibt noch mehr
Anwendungsbeispiele, von denen wir einige besprechen werden.
Allerdings hat diese Pal-Manipulation einen Nachteil:
Es entsteht ein Flackern, was nun wirklich unschn ist. Vermieden werden kann
es, indem man vorher diese Prozedur callt:

procedure WaitRetrace;assembler;
asm
    mov     dx,3DAh
@x: in      al,dx
    test    al,08h
    jnz     @x
@y: in      al,dx
    test    al,08h
    jz      @y
end;

Folgendes: Der Bildschirm wird alle soundsoviele Millisekunden wieder neu
aufgebaut: "Retraced". Wird nun whrend eines solchen Retrace das Bild
irgendwie verndert, so entsteht ein strendes Flackern. Dies kann vermieden
werden, indem man vor JEDER wichtigen Bildschirmnderung (Sprite zeichnen,
Palette verndern, etc.) den Retrace abwartet. Die Prozedur "weckt" das Input
Status Register 1 der VGA und sieht nach, wie das Bit 3 aussieht. Es zeigt
nmlich an, wie es um den Retrace steht. Die Prozedur wird solange geloopt,
bis der Retrace unten am Bildschirm angelangt ist, dann beendet, worauf man
die Palette verndern kann. Alles klar?
Ach brigens: Man kann die Palette auch auslesen:

procedure GetPal(col:byte;
                 var R,G,B:byte);
begin
  port[$3C7] := col;
  R := port[$3C9];
  G := port[$3C9];
  B := port[$3C9];
end;

Mit dieser Prozedur kann man die Farbwerte der Farbe col ermitteln.
So, war da nicht die Rede von anderen Anwendungsgebieten der Pal-Mod.?
Hier etwas sehr trickreiches: Wenn man nicht will, da der Anwender sieht,
was man mit der VGA anstellt, kann man das auch "unsichtbar" machen. Man setzt
einfach smtliche Farben auf Schwarz:

procedure BlackPal;
var n : byte;
begin
  WaitRetrace;
  for n := 0 to 255 do
    SetPal(n,0,0,0);
end;

Allerdings sollte man vorher seine Palettenwerte sichern, sonst kann man das
Bild schlecht wiederherstellen. Dazu deklarieren wir eine globale Variable
Pal : array[0..255,1..3] of byte. Das Sichern der Palette:

procedure GrabPal;
var n : byte;
begin
  for n := 0 to 255 do
    GetPal(n,Pal[n,1],Pal[n,2],
           Pal[n,3]);
end;

Dies schreibt smtliche Palettenwerte in das Array Pal. Wollen wir nun alle
Werte wieder herstellen, brauchen wir eine neue Prozedur namens RestorePal,
fr die ihr einfach das GetPal in ein SetPal verwandelt und nach dem "begin"
ein "waitretrace" einfgt.
Nachdem man die Palette mit GrabPal gerettet, den Bildschirm mittels BlackPal
abgedunkelt und das Bild (was auch immer das sein mag) auf den VGA
"geklatscht" hat, kann man auch eine andere, effektvollere Art des
Einblendens whlen, als einfach die Palette wiederherzustellen: Fading!
Fading ist ein Paletteneffekt, den man sehr oft zu sehen bekommt. Er
funktioniert folgendermaen: Nachdem man die oben beschriebenen Schritte
durchgefhrt hat und der Bildschirm schwarz ist, schreibt man die gewnschte
Zielpalette in die Variable Pal und erhht nun solange schrittweise die 
Palettenwerte, bis die Zielwerte in Pal erreicht sind. Das geht so:

procedure FadeUp;
var n1,n2 : byte;
    Tmp   : array[1..3] of byte;
begin
  for n1 := 1 to 64 do begin
    WaitRetrace;
    For n2 := 0 to 255 do begin
      GetPal(n2,Tmp[1],Tmp[2],Tmp[3]);
      if Tmp[1] < Pal[n2,1] then
        inc (Tmp[1]);
      if Tmp[2] < Pal[n2,2] then
        inc (Tmp[2]);
      if Tmp[3] < Pal[n2,3] then
        inc (Tmp[3]);
      SetPal(n2,Tmp[1],Tmp[2],Tmp[3]);
    end;
  end;
end;

Bei dieser Prozedur wird zunchst der Retrace abgewartet. Anschlieend wird
fr jede Farbe berprft, ob ihre Farbwerte schon denen in der Zielpalette
entsprechen. Wenn nicht, wird der Wert um 1 erhht. Danach wird der neue
Wert in die Palette eingetragen. Das Ganze wird 64 mal wiederholt.
Umgekehrt geht auch. Will man also ein Bild auf dem VGA langsam ausfaden,
schreibt man sich eine Prozedur wie oben, nur ersetzt man die INCs durch
DECs, die Kleiner-als- durch Grer-als-Zeichen, und die "Pal[n2,?]" durch 0.
So, nun knnen wir schon ein paar sehr schne Effekte erzielen, vielleicht
knnt ihr aus den vorgegebenen Routinen noch mehr machen (z.B ein Fading ins
Weie erzeugt den Eindruck einer Explosion). Ich hoffe ich werde in dem PCH,
in dem diese Anzi erscheint schon eine Menge Feedback lesen.
Nchstes Thema ist hchstwahrscheinlich...Scrollinx!!!
Na dann, bis zur nchsten Ausgabe.





[ 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.                                             ]
