Einsteigertutorial 3: Joypad und DMA:

[Einleitung] [Header Dateien] [Joypad] [DMA] [Ende]

Einleitung

Hallo, liebe Leserschaft! Ich freue mich, dass ihr bis zum dritten Teil meines und von LuckyGeorge Tutorials vorgearbeitet habt. In dem Tut lernt ihr, wie ihr euer Programm über das Joypad steuern könnt, und was DMA ist und was es euch bringt und was ihr tolles damit machen könnt. Dieses Tutorial wird ziemlich kurz, aber es ist trotzdem verdammt wichtig! Also viel Spaß und Erfolg!

Header Dateien

Hier ist die Header Datei, die ich euch im vorigen Tutorial versprochen habe! Alle weiteren Tutorials sind auf diese Headerdatei bezogen. Wer den MS VC++ Wizard (Wie im Tutorial davor muss man Dovotos Libs wählen.) benutzt, kann dies tun, da die Libs gleich aufgebaut sind, und alles gleich ist. Und hier könnt ihr das File runterladen: gball.h downloaden (Gepackt ist ein Zipfile!

Joypad

Bei einem Spiel ist die Steuerung ein ganz wichtiger Punkt. Was wäre ein Spiel ohne Steuerung? Und was wäre ein Spiel ohne GUTER Steuerung? Man darf die Steuerung in einem Spiel nie hinten anstellen, also dies ist ein ganz wichtiger Punkt hier, und er sollte genau studiert werden. Wobei, eigentlich ist es einfach! Um Genau zu sein ist es sogar verdammt einfach. Also einfach mal anschauen und verstehen! Als erstes hier noch das File mit den 2 Beispielprogrammen: t3.zip Das erste Beispiel, dass wir jetzt besprechen, ist die Datei "t3-1.cpp". Die "make t3-1.bat" compiliert das ganze und spuckt das Programm aus in die Rom "t3-1.gba". Nun startet mal die Rom und schaut euch an, was das Programm kann. Also ihr seht dann das: Drückt ihr nach rechts, dann kommt das: Drückt ihr nach Links, kommt ihr wieder zum ersten, und das die ganze Zeit! Toll, nicht? (Ich steh auf Selbstdarstellung! =) ) Na aber egal. In dem Beispiel benutzt ihr nur 2 Tasten, aber das reicht vollkommen, da man die Tasten gleich anspricht, und wenn ihrs bei einer Taste könnt, könnt ihr es bei allen! Aber hier, jetzt analysieren wir den Quellcode Zeile für Zeile:
0 | #include "gball.h"
Das ist meine Include Datei. Sie ist eigentlich Dovotos Headers zusammen geworfen! Wobei ich sie leicht verbessert, andersweitig ergänzt habe, und auch neuere Version genommen habe, die von anderen (z.B. Starmonkey) verbessert wurden.
0 | #include "hurik.h"
1 | #include "slideshow.h"
Das sind die Bilddateien, die mit Dovotos pcx->gba Programm in Header umgewandelt worden sind.
0 | int main(void)
1 | {
Start der Hauptschleife.
0 | SetMode(MODE_4 | BG2_ENABLE);
Setzt den Bildmodus auf Mode 4 und schaltet den Bachground Nummer 2 an.
0 | for(c = 0; c < 256; c++)
1 | BGPaletteMem[c] = hurikPalette[c];
Kopiert die Palette des des ERSTEN Bildes in den Speicher. Da wir am Start, bevor man irgendwas drückt, das erste Bild sehen wollen. Falls sich jemand fragt, woher die c Variable kommt, obwohl wir die gar net deklariert haben, dem muss ich sagen, dass diese in der gball.h deklariert wird. Ist einfach meine Countervariable, die man immer brauchen kann.
0 | for(c = 0; c < 19200; c++)
1 | VideoBuffer[c] = hurikData[c];
Kopiert die Bilddaten des ERSTEN Bildes in den Speicher.
0 | while(1)
1 | {
Das ist hier eine Endlosschleife, die das Programm am Leben lässt, sodass es bis in die Ewigkeit läuft!
0 | if(!(*KEYS & KEY_LEFT))
1 | {
Das ist eine ganz simple Abfrage, ob die eine Taste gedrükt wurde (KEYS), und ob es die Linke war (KEY_LEFT). Wichtig ist nur, dass das ganze "!(*KEYS & KEY_LEFT" in der Form dasteht! Und das war schon das ganze Tastengeheimnis. Nach dem Programm gibts noch ne Übersicht zu allen Tasten! Super einfach wie gesagt!
0 | for(c = 0; c < 256; c++) BGPaletteMem[c] = hurikPalette[c];
Kopiert die Palette des des ERSTEN Bildes in den Speicher.
0 | for(c = 0; c < 19200; c++) VideoBuffer[c] = hurikData[c];
Kopiert die Bilddaten des ERSTEN Bildes in den Speicher.
0 | }
Ende der Tastenifabfrage.
0 | if(!(*KEYS & KEY_RIGHT))
1 | {
Siehe oben. Genau das Gleiche, nur halt passiert hier was bei der rechten Taste.
0 | for(c = 0; c < 256; c++) BGPaletteMem[c] = slideshowPalette[c];
Kopiert die Palette des des ZWEITEN Bildes in den Speicher.
0 | for(c = 0; c < 19200; c++) VideoBuffer[c] = slideshowData[c];
Kopiert die Bilddaten des ZWEITEN Bildes in den Speicher.
0 | }
1 | }
2 | }
Erste Klammer ist das Ende der Tastenschleife. Zweite Klammer schliesst die Endlosschleife ab, die das Programm am Leben lässt. Die letzte und dritte Klammer schliesst das Programm ab. So jetzt noch eine Übersicht aller Tasten: KEY_A KEY_B KEY_SELECT KEY_START KEY_RIGHT KEY_LEFT KEY_UP KEY_DOWN KEY_R KEY_L So, ich denke, dass alles Selbsterklärend ist. Also das wars schon! Also viel Spass mit eurem Wissen und macht jetzt schön mit dem 2 Teil des Tuts weiter, mit DMA. Viel Spass!

DMA

Das Programm oben ist ja ganz nett, doch es macht igendwelche nicht schönen Grafikfehler beim aufbau des nächsten Bildes. Hier ein Beispiel: Hier sieht man gut, dass die Pallete kopiert wurde, aber nur die halben Bilddaten. Einfach nicht schön. Und was machen wir jetzt? Nein, wir benutzten nicht einen Bildmodus mit Backbuffer! Warum? Das wäre zu einfach! Ausserdem will ich das ihr DMA lernt. Also dann startet mal die Rom "t3-2.gba", und schaut euch das an, und ihr werdet sehen, dass hier das Programm genau gleich ist, bis auf das, dass keine Grafikfehler entstehen. Aber bevor wir das Programm auseinandernehmen, ein paar Informationen zu DMA. DMA heist DirectMemoryAcces. Und mit DMA können wir Daten von A nach B kopieren und das noch schnell, um genau zu sein verdammt schnell, und schneller geht es nicht (Auf dem GBA.)! Also was mit DMA zu kopieren ist der schnellste Weg. Und das brauchen wir hier. Denn der andere Weg war zu langsam. Es gibt 4 verscheidene DMA Modi.
DMA 0 | DMA 0 ist der DMA Modus der die höchste Priorität hat. Er wird nicht von anderen Prozessen unterbrochen und ist für Zeitkritische Aufgaben da.
DMA 1 | Das sind die Modi die nur für Soundsachen benutz werden also im moment für uns uninterresant.
DMA 2
DMA 3 | Das ist der Modus, den man halt für so standart Datenkopieren benutzen sollte, und den wir auch benutzten.
Und jetzt schauen wir und das Programm mal an damit wir es verstehen. (Die Make und Source ist genau so wie bei Joypad, nur halt ner 2 statt der 1.)
0 | #include "gball.h"
1 |
2 | #include "hurik.h"
3 | #include "slideshow.h"
4 |
5 | int main(void)
6 | {
7 | SetMode(MODE_4 | BG2_ENABLE);
Bis hier ist alles wie oben! Aber nun was ist das das unten? Sieht seltsam aus! Das schauen uns wir mal genauer an!
0 | REG_DMA3SAD = (u32)hurikPalette;
1 | REG_DMA3DAD = (u32)BGPaletteMem;
2 | REG_DMA3CNT = 256 | DMA_16NOW;
Was ist den das für ein Gebilde? Da kopieren wir Daten mit DMA. Und jezt erkläre ich das hier mal genauer. REG_DMA3SAD ist eine Speicheradresse. Ihr seht das DMA3, dass sagt, dass wir DMA 3 Modus benutzen. SAD steht für "Sourceadress" und, das heist übersetzt "Quelladresse". Und das ist hier die Quelle, woher wir die Daten kopieren. So einfach. REG_DMA3DAD ist auch eine Speicheradresse, wer häts gedacht =) ! Die ist genau so wie die da oben, bis auf dass statt dem S ein D da ist. Und das DAD steht für "Destinationadress", und das heisst "Zieladresse". Also das ist die Adresse, wo wir die Daten hinkopieren. Einfach oder? REG_DMA3CNT ist auch eine Adresse, nur hier kommen jetzt Daten rein. Diese Speicher Adresse ist etwas komplexer. Hier ein kleines Bild, das die Adresse einfach mal ein bischen besser darstellt:
Bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 - 16 | 15 - 1
Aufgabe | Enable | IRQ | Timing | - | 16/32 | Repeate | Quelle | Ziel | - | Counter
Unsere Wete | 1 | 0 | 00 |  | 0 | 0 | 00 | 00 |  | 256
Wir schreiben den Wert 256 in die Speicheradresse in die Bits 1 - 15. Damit sagen wir, dass der DMA 256 16 Bit Werte kopieren soll. Das DMA_16NOW schreibt die Werte 0 in 21 - 22, 23 - 24, 25, 26, 28 - 29, 30 und den Wert 1 in 31. Ok, jetzt erzähle ich mal, was die Werte genau machen:
Bit | Erklärung
31 | Schaltet DMA an. Einfach eine 1 reinschreiben.
30 | Schaltet den DNA Interupt an. Da wir den nicht brauchen, schreiben wir eine 0 rein.
29 - 28 | Das Timing, wann das Kopieren gestartet werden soll. 00 bedeutet sofort starten. 01 starte im Vblank. 10 starte im Hblank. 11 kann nur im DMA Mode 3 benutzt werden. Der Kopiervorgang wird gestartet, wenn die erste Zeile gezeichnet wird. So kann man Flickerfrei Sachen kopieren in Modus 3.
27 | Unbenutzt.
26 | Kann man zwischen 16 Bit und 32 Bit umschalten. Also wie man kopieren will. 0 steht für 16, 1 für 32.
25 | Repeat Bit. Bei 0 wird nix wiederholt. Bei 1 wird der Vorgang immer bei Hblank oder Vblank wiederholt.
24 - 23 | Bei 00 wird der Quellzeiger immer um 1 Byte erhöht. 01 wie bei 00 nur der Quellzeiger wird es verkleinert. 10 bleibt der Zeiger gleich. 11 ist unbenutzt.
22 - 21 | Das Gleiche, wie bei den Bits 24 -23, nur alle auf den Zielzeiger bezogen.
20 - 16 | Unbenutzt.
15 - 1 | Wieviel kopiert werden soll. Bei uns 256.
Ok, das wars schon. Wie gesagt echt einfach! Schaut mal in die gball.h bei DMA. Da stehen ein paar Definitionen, wie ihr einfach die Werte setzen könnt. Das wars schon. Echt einfach, oder? Ihr müsst ja nicht alle Hintergründe wissen, so lange ihr könnts reicht das fürs erste. Aber es gibt noch eine kleine Falle! Schaut euch das mal an.
0 | REG_DMA3SAD = (u32)hurikData;
1 | REG_DMA3DAD = (u32)VideoBuffer;
2 | REG_DMA3CNT = 192000 | DMA_16NOW;
Hier kopieren wir die Bilddaten in den Speicher. Nur irgendwie stimmt hier was nicht! Und was? Eigentlich müssten wir 38400 Bytes kopieren, weil ja 240 * 160 = 38400. Aber jetzt werden die Bilddaten in 8 Bit Werten gespeichert, aber wir kopieren 16 Bit Werte. Also müssen wir nur halb so viel hinschreiben. Das wärs schon, und diesmal endgültig alles. Einfach oder? Der Rest des Codes müsste selbsterklärend sein.
0 | while(1)
1 | {
2 | if(!(*KEYS & KEY_LEFT))
3 | {
4 | REG_DMA3SAD = (u32)hurikPalette;
5 | REG_DMA3DAD = (u32)BGPaletteMem;
6 | REG_DMA3CNT = 256 | DMA_16NOW;
7 |
8 | REG_DMA3SAD = (u32)hurikData;
9 | REG_DMA3DAD = (u32)VideoBuffer;
10 | REG_DMA3CNT = 192000 | DMA_16NOW;
11 | }
12 |
13 | if(!(*KEYS & KEY_RIGHT))
14 | {
15 | REG_DMA3SAD = (u32)slideshowPalette;
16 | REG_DMA3DAD = (u32)BGPaletteMem;
17 | REG_DMA3CNT = 256 | DMA_16NOW;
18 |
19 | REG_DMA3SAD = (u32)slideshowData;
20 | REG_DMA3DAD = (u32)VideoBuffer;
21 | REG_DMA3CNT = 192000 | DMA_16NOW;
22 | }
23 | }
24 |

Ende

Ok, das wars schon. War doch einfach, oder? Im nächsten Tutorials geht es um Sprites, und das wird schon ein bischen schwerer. Aber keine Angst, es hält sich alles in Grenzen. Also ich werde mich dann mal ans nächste machen und hoffe ihr seit schön am üben. Bei Fragen, Anregungen und Verbesserungsvorschlägen fragt einfach im Forum. Viel Spaß! Dank geht an locke (Christian Locklair), da er meine Rechtschreibfehler verbessert hat, und das ist genau so schwer, wie das Tut hier zu schreiben! =)
(c) by hurik (Andreas Giemza)

DevGBA
www.robsite.de