PlanetSwitch Planet3DS PlanetVita PSP.de PlanetiPhone Classics Forum

PGN-ID:[?] (Nicht eingeloggt)
Login
Registrieren
PlanetDS PlanetGameboy N-Page.de

Einsteigertutorial 2: Einführung in die Screenmodes

zurück

Autor: LuckyGeorge

Kategorie: devtut
Umfang: 1 Seiten

Seite   1  

Kommentare:

Kommentieren (-)

Game Boy Advance Artikel vom 29.03.2002






























































Einführung
in die
Screenmodes (V1)
Inhaltsverzeichnis:


Einleitung

... noch ein Wort zum Compiler

Mode 3

Mode 4

Mode 5

Ein kleiner Bildbetrachter

... und ich kann doch zählen

Die Programme downloaden (Source + Rom)

Einleitung

Nach dem Ihr im ersten Teil des Tutorials schon mal
ein kleines GBA Programm geschrieben und getestet habt, versuchen wir
euch im zweiten Teil die Grafikschnittstelle des GBA ein wenig näher
zu bringen. Wir werden versuchen die Grundbegriffe der Grafik zu erläutern
und einen kleinen Bildbetrachter für den GBA zu programmieren. Solltet
Ihr irgendwann im Text mal den Faden verlieren, so braucht Ihr keine
Angst zu haben. Es sieht oft komplizierter aus als es ist und das Verständnis
kommt spätestens nach dem Ihr die Texte ein zweites mal gelesen habt.
Bloß nicht aufgeben. Aller Anfang ist bekanntlich schwer.


Vorneweg möchte ich noch bemerken, das viele Anregungen und Sourcen,
die ich verwendet habe von Dovoto und seinem „Pern Project“ stammen.
Besucht ihn doch mal unter:


href="http://emuholic.emulous.com/%7Edovoto/" target="_blank">http://emuholic.emulous.com/~dovoto/

...
noch ein Wort zum Compiler

Wer noch nie mit dem Microsoft Visual Studio gearbeitet
hat sollte diesen Abschnitt überspringen.


Im ersten Teil wurde die Installation des Compilers schon ausführlich
besprochen. Hier noch ein kleiner Tip: Wer so wie ich das arbeiten mit
dem Microsoft Visual Studio gewöhnt ist, braucht sich davon nicht zu
trennen. Es gibt einen GBA Project Wizard, mit dem Ihr alle Programme
im Visual Studio programmieren und kompilieren könnt:


href="http://emuholic.emulous.com/~dovoto/GBA_AW_r2-6.zip">http://emuholic.emulous.com/~dovoto/GBA_AW_r2-6.zip


Einfach die Datei herunterladen und in das Template Verzeichnis des
Visual Studios entpacken (meist „C:\Programme\Microsoft Visual Studio\Common\MSDev98\Template“).
Wenn Ihr nun das Visual Studio startet habt Ihr bei den Projekten eine
neue Auswahl „GBA Project“. Beim Start des Wizards müsst Ihr erst das
Verzeichnis des GBA Development Kits angeben ( „C:\Devkitadv\“) und
dann die Bibliotheken die Ihr verwendet wählen. Wählt am besten die
des „PERN Projects“ bei CRT und HEADERS.


Achtung bei der Wahl des Projektverzeichnisses. Es dürfen keine Leerzeichen
im Verzeichnisnamen vorkommen.


Nach der Installation und dem anlegen des Projektes habt Ihr sämtliche
Vorzüge des Visual Studios zur Verfügung. Zu beachten ist hier allerdings,
das die fertige Datei nicht im DEBUG Verzeichnis erscheint sondern im
Projektverzeichnis. Außerdem gibt der Compiler nur die Zeilennummern
mit Fehlern aus. Es ist nicht möglich einfach durch Klicken auf den
Fehler an die entsprechende Zeile im Sourcecode zu springen.

Mode
3

Dieser Mode ist wohl am einfachsten zu verstehen.
Wir haben Ihn ja auch schon in unserem ersten Programm verwendet. Der
Bildschirm besteht hier einfach aus einem 240*160 = 38400 Pixel großem
Array. Dieses Array ist eindimensional, das heißt wir können nicht einfach
die x und y Koordinaten eines Pixels angeben, sondern müssen uns das
Bild so vorstellen, dass alle Zeilen hintereinander stehen. Der Index
des 4. Pixels in der 5. Zeile errechnet sich also wie folgt: (4 – 1)
+ ( (5 – 1) * 240 ) = 963. Um also genau an diesem Pixel einen Punkt
zu setzen wäre folgendes Programm notwendig:


#include "gba.h"

#include "screenmode.h"


int main(void)

{

        SetMode(MODE_3 | BG2_ENABLE);

        VideoBuffer[963] = (31
<< 10);

        while(1);

}


Wem die Funktion der einzelnen Befehle noch nicht klar ist, der sei
an den ersten Teil des Tutorials verwiesen.


In diesem Modus besteht jedes Pixel aus 16Bit. Das heißt wir müssen
die unterschiedliche Farben aus den Anteilen Rot, Grün und Blau zusammensetzen.
Um nicht ständig aufwendig umrechnen zu müssen hat Dovoto ein kleines
Makro geschrieben, das aus den unterschiedlichen Werten für Rot, Grün
und Blau einen 16Bit Wert erzeugt:


#define RGB(r,g,b) ((r)+((g)<<5)+((b)<<10))


Die einzelnen Farbanteile können Hier Werte zwischen 0 und 31 annehmen.
Um zum Beispiel das oben gesetzte Pixel in reinem Blau anzuzeigen müssen
wir die Zeile mit dem setzen des Pixels wie folgt modifizieren:


VideoBuffer[963] = RGB(0 ,0 ,31);


Nun solltet Ihr in der Lage sein verschiedene Farben auf den Bildschirm
zu zeichnen. Versucht doch mal folgendes Programm zu verstehen:


#include "gba.h"

#include "screenmode.h"


#define RGB(r,g,b) ((r)+((g)<<5)+((b)<<10))


unsigned short int * videobuffer =
(unsigned short int *) 0x6000000;


int main(void)

{

      SetMode(MODE_3 | BG2_ENABLE);

      for(int i=0; i< 38400; i++)

      {

                  if(((i%240) > 80) && ((i%240) <= 160)
)

                             videobuffer[i]
= RGB(0,0,31);

                  else if((i%240) > 160)

                            
videobuffer[i] = RGB(0,31,0);

                  else

                             videobuffer[i]
= RGB(31,0,0);

      }

      while(1);

}


Dieses Programm zeichnet drei verschiedene Balken auf dem Bildschirm.
Das Ergebnis sollte so aussehen:


src="http://andreas.abi-2005.org/devgba/artikel/hurik/dt2/t2-1.gif" v:shapes="_x0000_i1026">


Was genau macht also das Programm. Das Macro RGB habe ich oben ja schon
erklärt. Die Zeile:


unsigned short int * videobuffer = (unsigned short int
*) 0x6000000;


sagt dem GBA an welcher Adresse sich der Videospeicher befindet. Eine Adresse
ist nichts anderes als ein Wegweiser für den GBA, der ihm sagt an welcher
Stelle im Speicher er etwas schreiben oder lesen soll.


Mit dem Ausdruck


i%240


bestimme ich den sog. Modulo zu 240. Ich teile den Wert i durch 240 und lasse
mir den ganzahligen Rest ausgeben. Wenn i z.B den Wert 500 annimmt,
dann ergibt i%240 den Wert 20, da die Zahl 500 den Wert 240 zwei mal
enthält, aber 2*240 = 480 ist, fehlen mir genau 20 um den Wert 500 zu
erreichen.


Was bringt mir der Modulo an dieser Stelle? Erinnert Ihr euch, das ich auf
die x und y Koordinate eines Pixels nicht direkt zugreifen kann? Um
also die x-Koordinate eines Pixels zu bestimmen muss ich den Index des
Bildspeichers mit der Breite einer Zeile umrechnen. Und genau das macht
der Modulo. Er gibt mir den x-Wert eines Pixels, das ich nur durch den
Index des Bildspeichers gegeben habe. Und mit der Abfrage, ob dieser
x-Wert kleiner als 80 bzw. größer als 160 ist, kann ich den Bildschirm
in drei gleich große Teile zerlegen.


Soviel zum Zeichnen in Mode 3. Obwohl er der einfachste
Modus ist, sollte man ihn nicht so häufig verwenden. Da für jeden Pixel
ein 16Bit Wert benötigt wird brachen wir 16 * 38400 = 614400 Bit oder
75kByte Speicher um ein Bild darzustellen. Der Grafikspeicher des GBA
ist jedoch nur 96kByte groß, so das wir immer nur ein Bild im Speicher
haben können. Da der Modus 3 zudem auch noch recht langsam ist, ist
es nicht möglich Animationen im Mode 3 abzuspielen. Er ist also nur
für Standbilder geeignet.

Mode
4

Der Mode 4 ist nicht viel komplizierter
zu verstehen als der Mode 3. Ähnlich wie im Mode 3 können wir die Pixel
direkt einfach durch Angabe der Adresse im Speicher zeichnen. Im Unterschied
zum Mode 3 geben wir hier jedoch nicht direkt die Farbe des Pixels an,
sondern greifen auf eine sogenannte Palette zurück. Eine Palette ist
ähnlich wie der Bildspeicher ein eindimensionales Array umfasst jedoch
nur 256 Einträge. In jedem Feld dieses Arrays steht ein 16Bit Farbwert
und man gibt dann beim Zeichnen nicht mehr den Farbwert selbst an, sondern
nur den Index der Farbe in der Palette.


Man kann sich die Palette wie einen Malkasten mit 256 verschiedenen
Farben vorstellen. Um zu zeichnen braucht man hier nicht die Zusammensetzung
der Farbe zu kennen sondern muss nur wissen an welcher Stelle des Malkastens
die Farbe sich befindet. Um Gegensatz zum echten Leben kann man allerdings
die Farben nicht mischen.


Im Gegensatz zum Mode 3 hat man aber nun nicht mehr 32*32*32 = 32768
Farben zur Verfügung sondern nur noch die 256 Farben, die in der Palette
gespeichert sind. Warum sollte man also diesen Modus verwenden?


Ganz einfach. Dieser Modus ist viel schneller als der Mode 3 und belegt
weniger Speicher. Da wir nur noch 8Bit für einen Pixel benötigen braucht
ein Bild im Grafikspeicher auch nur 8*38400 = 37,5kByte. Dadurch können
wir zwei komplette Bilder im Videospeicher ablegen. Man kann also ein
Bild im Speicher zeichnen, während das zweite gerade angezeigt wird.
Nur in diesem Modus sind Bildschirmfüllende Animationen möglich.


So, nun genug von den Vorteilen. Wir wollen mal versuchen ein kleines
Programm zu schreiben, das im Mode 4 arbeitet.


Wir wollen nun ein Bild mit einer Palette auf dem GBA darstellen. Da
das „Zeichnen“ eines Bildes nur durch Angabe der Pixelpositionen und
Farben auf Dauer etwas mühsam ist, verwende ich hier ein Programm da
mir ein Bild in den entsprechenden Quellcode umrechnet. Es gibt sehr
viele solcher Programme und meine Wahl ist auf den GBA Gfx Converter
gefallen: http://www.gbadev.org/files/AGBGFXCon.zip


In diesem Programm könnt Ihr ein Bild der Größe 240*160 Pixel mit 256
Farben laden und umwandeln. Ihr bekommt dann das Bild als .C Datei.
Diese Datei nennt Ihr am geschicktesten nach .h um und entfernt die
zweite Zeile, in der steht:


#include


Standardmäßig erzeugt AGBGFXCon zwei Arrays namens Palette und Map
in denen die Palette des Bildes und die Bilddaten gespeichert werden.
Beim folgenden Programm habe ich mich für das Planetgameboylogo ( href="http://www.planetgameboy.de/" target="_blank">http://www.planetgameboy.de)
entschieden. Doch nun zum eigentlichen Programm:


#include "gba.h"

#include "screenmode.h"

#include "PGLight.h"


u8*   videobuffer   =          (u8*)0x6000000;

u16* palettebuffer =          (u16*)0x5000000;


void WAIT(int x) {for(int i=0;i


int main(void)

{

      int i, ,j=0;



      SetMode(MODE_4 | BG2_ENABLE);


      for(i=0;i<256;i++)

                  palettebuffer[i] = PGLight_Palette[i];


      for(i=0;i<38400;i++)

                  videobuffer[i] = PGLight_Map[i];


      while((*KEYS) & KEY_A);


      while(1)

      {


                  for(i=0;i<256;i++)

                        if((i+j) < 255)

                               
palettebuffer[i] = PGLight_Palette[i+j];



                  j++;



                  if(j==256)

                           
j=0;



                  WAIT(3000);

       }

}


Dieses Programm sollte folgendes auf dem Bildschirm darstellen:


src="http://andreas.abi-2005.org/devgba/artikel/hurik/dt2/t2-2.jpg" v:shapes="_x0000_i1027">


Sobald man jedoch die Taste A auf dem GBA drückt, sollte das ganze Bild wie
wild anfangen zu flimmern.


Wie genau funktioniert dieses Programm? Als erstes fällt auf, das ein neuer
Header hinzugekommen ist:


#include “PGLight.h“


In diesem Header befinden sich die beiden vom GBAGFXCon angelegten Arrays
mit der Palette und dem eigentlichen Bild. Auch die Zuweisung der Adressen
hat sich geändert:


u8*   videobuffer       =          (u8*)0x6000000;

u16* palettebuffer    =          (u16*)0x5000000;


Neben der schon bekannten Adresse für den Bildspeicher ist nun noch eine Adresse
für die Palette hinzugekommen. Immen dran denken, alles was Ihr in diesen
Speicherbereich schreibt hat direkte Auswirkungen auf die Anzeige. Wir
werden diesen Umstand noch ausnutzen.


Der Variabelentyp des Bildspeichers ist nun nicht mehr u16 ( unsigned int
short) sonder u8 (unsigned int char). Das liegt daran, das jetzt im
Bildspeicher keine 16bit Farben mehr abgelegt werden, sondern nur noch
die Einträge in die Palette. Und diese hat nur 256 Einträge, also jeder
Eintrag hat 8Bit.


Mit:


SetMode(MODE_4 | BG2_ENABLE);


Sagen wir den Gameboy, dass wir im Mode 4 arbeiten wollen.


Anschließend werden alle Einträge der Palette in den Palettenspeicher eingetragen
und alle Bilddaten in den Bildspeicher:


for(i=0;i<256;i++)

      palettebuffer[i] = PGLight_Palette[i];


for(i=0;i<38400;i++)

      videobuffer[i] = PGLight_Map[i];


Die folgende Zeile:


while((*KEYS) & KEY_A);


sagt dem GBA, daß er warten soll bis die Taste A gedrückt wird. Eine genaue
Beschreibung der Tasten und Eingaben werden wir in einem späteren Teil
des Tutorials behandeln.


while(1)

{

      for(i=0;i<256;i++)

               if((i+j) <
255)

                        palettebuffer[i]
= PGLight_Palette[i+j];


      j++;


      if(j==256)

              
j=0;


      WAIT(3000);

}


Diese Schleife führt ein sogenanntes Palette-Looping durch. Dabei wird dem
Palettenspeicher immer eine neue um einen Wert verschobene Palette zugewiesen.
Bei geschickter Wahl der Palette und des Bildes kann man so interessante
und vor allem schnelle Animationen erstellen, da der Bildschirm nicht
neu gezeichnet werden muss. Insbesondere Aus- und Einblendeffekte werden
so realisiert. Die Funktion WAIT()ist hier nur ein kleiner Zeitbegrenzer,
der dem GBA sagt, das er 3000 Additionen durchführen soll, bevor er
weiter macht.


Wie Ihr seht ist auch der Mode 4 noch recht einfach zu handhaben.

Mode
5

Kommen wir nun zum letzten der einfachen
Grafikmodi. Der Mode 5 ist eine Mischung aus Mode 3 und Mode 4. Genauso
wie im Mode 3 besteht jedes Pixel aus einem 16Bit Farbwert. Dennoch
verbraucht ein Bild im Mode 5 weniger Speicher als im Mode 3. Das liegt
daran, dass jedes Bild hier nur 160*128 Pixel groß ist. Man hat also
die Möglichkeit zwei Bilder im Videospeicher abzulegen bzw. ein Bild
im Speicher zu zeichnen während das andere dargestellt wird. Das folgende
Programm soll euch mit dem Verfahren des double bufferings bekannt machen.
Dazu müssen wir zunächst die Adressen für das erste und das zweite Bild
definieren:


#define ErstesBild             (u16*) 0x6000000

#define ZweitesBild          (u16*) 0x600A000



u16* videobuffer;


Wie Ihr seht steht der Videospeicher hier nicht mehr an einer festen Adresse.
Die Adressen sind nun durch die beiden Variablen ErstesBild und ZweitesBild
vorgegeben.


Des weiteren kommen zwei neue Funktionen vor. Beide Funktionen stammen
von Dovoto und ich habe versucht Sie etwas verständlicher zu gestalten.
Die erste Funktion:


void WechsleBild(void)

{

      if(REG_DISPCNT & BACKBUFFER)

      {

                  REG_DISPCNT &= ~BACKBUFFER;

                  videobuffer = ZweitesBild;

      }

      else

      {

                  REG_DISPCNT |= BACKBUFFER;

                  videobuffer = ErstesBild;

      }

}


überprüft, welches Bild gerade angezeigt wird und wechselt dann das anzuzeigenden
Bild. Je nachdem, ob das Statusbit BACKBUFFER ( in der gba.h definiert
) gesetzt ist oder nicht, setzt diese Funktion dieses Bit zurück und
weist der Adresse des Videospeichers die Adresse des ersten oder zweiten
Bildes zu.


Die zweite Funktion:


void WarteAufAustastluecke(void)

{

      #define ScanlineCounter *(volatile u16*)0x4000006


      while(ScanlineCounter < 160);

}


wartet, bis ein Bild fertiggezeichnet wird. Der Begriff Austastlücke ist hierbei
nicht ganz so glücklich gewählt. Dieser Begriff stammt aus der Fernsehtechnik
und bezeichnet ein bestimmtes Videosignal das dem Fernseher sagt, dass
ein Bild dargestellt wurde. An der Adresse von ScanlineCounter speichert
der GBA immer den Index der aktuellen Zeile, die er gerade zeichnet.
Um also zu warten, bis ein Bild komplett gezeichnet wurde müssen wir
warten bis der Wert von ScanlineCounter 160 geworden ist.


Das Hauptprogramm sieht wieder recht einfach aus:


int main(void)

{

      int i;


      int Endpixel = 20480;


      SetMode(MODE_5 | BG2_ENABLE);


      videobuffer = ErstesBild;


      for(i=0;i
                  videobuffer[i] = RGB(255,0,0);


      videobuffer = ZweitesBild;


      for(i=0;i
                  videobuffer[i] = RGB(0,255,0);


      while(1)

      {

                  WarteAufAustastluecke();

                  WechsleBild();

                  while((*KEYS) & KEY_A);

      }

}


Hier zeichnen wir erst ein komplett rotes Bild in den Bildspeicher des ersten
Bildes und danach ein komplett grünes Bild in den Bildspeicher des zweiten
Bildes. Die Variable Endpixel ergibt sich daraus, dass im Mode 5 das
Bild nur noch 160*128 = 20480 Pixel groß ist. Ihr könnt diese Variable
beliebig vergrößern und werdet bis auf etwaige Abstürze nichts erleben.


Das Programm startet danach eine Endlosschleife, die nichts anderes macht,
als zwischen den beiden Bildern hin- und herzuwechseln, wenn Ihr die
A-Taste des GBA drückt. Wenn Ihr die Funktion WarteAufAustastLuecke()weglast,
so bekommt Ihr ein schönes Streifenmuster beim drücken der A-Taste.
Es ist also beim  double buffering absolut notwendig, daß Ihr immer
wartet, bis ein Bild gezeichnet wurde.

Ein
kleiner Bildbetrachter

Mit den nun geschilderten Funktionen der unterschiedlichen
Grafikmodi ist es möglich einen kleinen Bildbetrachter zu schreiben.
Dazu erstellen wir zunächst mit dem Programm AGBGFXCon 5 kleine Bilder
und binden Sie in das Projekt ein. Beachtet, das Ihr die Dateien von
.c nach .h umbenennen müsst und die zweite Zeile mit dem Include in
jeder Date entfernen müsst.


Zur Darstellung empfehle Ich den Mode 4. Die Bilder laden zwar fast
zu schnell, aber man kommt mit weniger Speicher aus. Und man sollte
sich gleich angewöhnen, nicht zu verschwenderisch mit dem Speicher umzugehen.
Ich erspare es mir hier, das ganze Programm abzudrucken. Das Dokument
würde dann einfach zu lang.

...
und ich kann doch zählen

Wer sich jetzt vielleicht schon fragt,
wo die übrigen  Screenmodes geblieben muss leider auf das nächste mal
warten. Die Modi 0 – 2 sind von den Modi 3 – 5 so verschieden, dass
Sie ein eigenes Kapitel verdienen. Auch die Abfrage der Tastatur und
die Steuerung von Interrupts können noch einige Kapitel füllen.


So das war's erst mal. Ich hoffe ihr habt grob verstanden, worauf es
bei der Verwendung von Screenmodes ankommt und könnt schon selbst
ein wenig mit der Grafik des GBA herumexperimentieren.

Die
Programme downloaden (Source + Rom)
Hier giebts noch die ganzen Programme zum
download. Ich (Hurik =) Ich mach hier die Fummel arbeit!) hab sie mal
geordnet ob sie mit oder ohne Microsoft Visual C++ gemacht worden sind.






















Mit Microsoft Visual C++ Ohne Microsoft Visual C++
Mode
3
Mode 3
Mode 4 Mode 4
Mode 5 Mode 5
Bildbetrachter Bildbetrachter


Achso noch was wichtiges! LuckyGeorg benutzt in seinem Tutorial
(Das habt ihr grad gelesen!) hier andere Header Dateien als ich in meinem.
Das ist sehr unpraktisch und könnte Anfänger verwirren. Tut
es warscheinlich auch =) ! Sorry. Deswegen: Im nächsten Tut führen
wir einheitliche Headers ein. Damit wir euch net verwirren also ladet
sie euch runter. Aber was ihr hier gelernt habt bleibt so. lso es ändert
sich nix.

(c)
by Daniel "LuckyGeorge" Winkler



< vorige Seite Seite   1   nächste Seite >