GameGear Programmier-Einführung

Aus portablegaming.de Wiki
(Weitergeleitet von GameGear Einführung)
Wechseln zu: Navigation, Suche

Dieser Artikel ist ein Artikel, der dem alten PortableDev zur Verfügung gestellt wurde. Er ist Eigentum des jeweiligen Autors und steht nicht unter der Creative Commons-Lizenz des Wikis!



Vorlage:Box Wichtig


Vorlage:Box

Benötigte Ausrüstung

Vorlage:Box

Programmierung

Die Memory Map

Vorlage:Box

Die Memory Mapping Register

Vorlage:Box

Die Memory Map definieren

Vorlage:Box


Startup-Code

Am Anfang eines jeden ROMs, also beim Reset, sollten etwa folgende Instruktionen stehen:
  .BANK 0 SLOT 0
  .ORG $0000
  di
  im 1
  ld sp, $dff0
  jp Start

di schaltet erstmal die Interrupts aus, die man während des Initialisierens sowieso nicht gebrauchen kann.

im 1 schaltet den Interrupt-Modus auf 1, wodurch der Z80 bei Interrupts immer nach $0038 springt. Mehr dazu kommt später im Kapitel über Interrupts.

ld sp, $dff0 setzt den Stackpointer auf das Ende des RAMs. Der richtige Wert wäre eigentlich $e000, aber wegen den Memory Mapping Registern benutzt man $dff0. Ansonsten würde jeder Schreibzugriff auf die Memory Mapping Register den Stack überschreiben.

jp Start springt zu einem Label Start. Mit diesem Sprung hält man sich sowohl die IRQ- und NMI-Handler ($0038 und $0066), als auch die Bereiche für die RST-Instruktionen ($0008 bis $0038) frei.

Als nächstes sollte man die Memory Mapping Register initialisieren, da diese beim Reset womöglich nicht die richtigen Werte haben.

  Start:
     ld hl, $fffc
     ld (hl), 0
     inc l
     ld (hl), 0
     inc l
     ld (hl), 1
     inc l
     ld (hl), 2


Die ersten 48 KiB des ROMs liegen nun auf den ersten 48 KB des Adressbereichs und das SRAM ist ausgestellt. Dieser Codeteil sollte natürlich noch innerhalb des festen ersten KiB des ROMs sein, weil die Werte der Memory Mapping Register bis dahin unbekannt sind.

Wenn man will, kann man z.B. auch noch RAM und VRAM mit 0 leeren, weil diese sonst nur zufällige Werte enthalten.


Grafik

Als nächstes wollen wir Grafik auf dem Bildschirm ausgeben. Dazu erstmal eine Übersicht über den VDP.

Übersicht über den VDP

Der VDP im Game Gear bietet:

  • Eine Auflösung von 160x144 Pixeln
  • Eine frei scrollbare Tilemap mit einer Größe von 32x28 Tiles
  • 64 Sprites mit einer Größe von 8x8 oder 8x16 Pixel, 8 pro Zeile
  • Eine Farbpalette mit 32 aus 4096 Farben
  • 16 KiB Speicher
  • Frame und Line Interrupts

Diese Daten beziehen sich auf den normalerweise benutzten Videomodus M4, der in fast allen Game Gear Spielen benutzt wird. Es gibt aber noch ein paar andere Videomodi, wo das ganze etwas anders aussieht.

In den 16KiB Video-RAM des VDP befinden sich die Grafiken für die 8x8 Pixel grossen Tiles, die Tilemap und die Sprite-Tabelle. Die Tile-Grafiken werden sowohl für die Tilemap, als auch für die Sprites benutzt. Die Farbpalette ist in einem eigenen kleinen Speicher.

Ein Tile besteht aus 16 Farben und belegt daher 32 Bytes. In die 16KiB Video-RAM passen eigentlich 512 Tiles, da aber Tilemap und Sprite-Tabelle auch etwas Platz brauchen, sind es nur 450.

Kontrolle über das ganze hat man mit den VDP-Registern. Mit diesen legt man den Videomodus, die Interrupts, das Scrolling, und wo sich was im Video-RAM befindet, fest.

Mit folgenden I/O-Ports greift man auf den VDP zu:

  • Port $BE: Datenport (lesen und schreiben)
  • Port $BF: Kontrollport (lesen und schreiben)
  • Port $7E: V Counter (nur lesen)
  • Port $7F: H Counter (nur lesen)

In den nächsten Kapiteln steht, wie man diese Ports benutzt.

VDP-Register

Als Erstes sollte man die VDP-Register setzen. Es gibt folgende VDP-Register:

  VDP Register  0: Videomodus, Interrupts
  VDP Register  1: Videomodus, Interrupts
  VDP Register  2: Adresse der Tilemap
  VDP Register  3: Auf $FF setzen
  VDP Register  4: Auf $FF setzen
  VDP Register  5: Adresse der Sprite-Tabelle
  VDP Register  6: Adresse der Sprite-Tiles
  VDP Register  7: (Overscan-Farbe)
  VDP Register  8: X-Scrolling
  VDP Register  9: Y-Scrolling
  VDP Register 10: Line Interrupt-Zähler

In ein VDP-Register schreibt man wie folgt:

  • 1. Man sendet den Wert, den man in ein VDP-Register schreiben will an Port $bf.
  • 2. Man sendet den Wert %1000pppp an Port $bf, wobei pppp die Nummer des VDP-Registers ist.

Auslesen kann man die VDP-Register nicht.

Beispiel:

  ld  a, 50
  out ($bf), a
  ld  a, $89
  out ($bf), a

Dieses Beispiel setzt das Y-Scrolling auf 50.

Bei Systemstart haben die VDP-Register bestimmte Werte. Man sollte sich aber nicht darum kümmern, was genau das für Werte sind, sondern einfach alle überschreiben. Dazu könnte man folgenden Code benutzen:

  ; HL zeigt zu einer Tabelle, in der die VDP-Register-Werte stehen
  SetVDPRegisters:
  ld   c, $80           ; VDP-Register 0
  ld   b, 11            ; Es gibt 11 VDP-Register
  Loop:
  ld   a, (hl)
  inc  hl
  out  ($bf), a
  ld   a, c
  inc  c
  out  ($bf), a
  djnz Loop
  ret
  
  vdp_regs:
  .db ... (folgt)

Video-RAM schreiben und lesen

Mal angenommen, man will an einer bestimmten Adresse a (zwischen $0000 und $3FFF) ins Video-RAM schreiben, dann geht man folgendermaßen vor:

  • 1. Man sendet das Lowbyte von a an Port $bf
  • 2. Man sendet das Highbyte von a, ge-odert mit $40, an Port $bf
  • 3. Man sendet alle zu schreibenden Bytes an Port $be

Das Lesen des Video-RAMs geht ähnlich:

  • 1. Man sendet das Lowbyte von a an Port $bf
  • 2. Man sendet das Highbyte von a an Port $bf
  • 3. Man liest soviele Bytes, wie man will, von Port $be

Der VDP verwaltet für das Schreiben und lesen einen Zeiger, der nach jedem Schreib- oder Lesezugriff um 1 erhöht wird. Daher kann man direkt ganze Blöcke schreiben oder lesen, ohne diesen Zeiger dauernd neu setzen zu müssen.

Hier ein Beispiel, um die ersten 32 Bytes des Video-RAMs mit 0en zu füllen:

  ld  a, $00
  out ($bf), a
  ld  a, $40
  out ($bf), a
  ; Jetzt ist der VDP-Pointer auf $0000 gesetzt
  
  ld  b, 32
  xor a
  Loop:
  out ($be), a
  djnz Loop

Vorlage:Box Wichtig

Der kleine Codeteil, um den VDP-Pointer für das Lesen oder Schreiben zu setzen, wird oft benutzt, weshalb man sich daraus in der Regel eine Funktion schreibt. Diese eignet sich besonders dazu, am Anfang des ROMs, z.B. an der Adresse $0010, abgelegt zu werden, so daß man sie einfach mit rst $10 aufrufen kann. Die Funktion ist dann auch immer erreichbar, weil das 1 KiB des ROMs fest ist. Das sieht dann so aus:

  ; Setze VDP Pointer auf den Wert von HL
  .ORG $0010
  ld  a, l
  out ($bf), a
  ld  a, h
  out ($bf), a
  ret
  
  ...
  ; Später im Programm:
  ld  hl, $4000
  rst $10
  ; Jetzt ins Video-RAM an Adresse $0000 schreiben...
  ...

Wie man sieht, sendet die Funktion einfach HL an Port $bf.

Palette

Der Game Gear hat eine Palette mit 32 Farben, die sich jeweils mit 12 Bits (je 4 Bits für Rot, Gruen und Blau) bestimmen lassen. Ein Tile in der Tilemap benutzt entweder die Farben 0 bis 15 oder 16 bis 31, während Sprites immer die Farben 16 bis 31 benutzen.

Im Paletten-RAM belegt jede Farbe ein 16 Bit-Wort, von dem immer 4 Bits unbenutzt sind. Das Farbformat ist:

  MSB          LSB
  ----BBBBGGGGRRRR

In die Palette zu schreiben, geht ähnlich wie das normale Schreiben ins Video-RAM. Wenn f die erste Farbe ist, die man ändern will, geht man so vor:

  • 1. Man sendet (f * 2) an Port $bf
  • 2. Man sendet $C0 an Port $bf
  • 3. Man sendet für jede Farbe, die man ändern will, 2 Bytes an Port $be

Die Farben müssen im Little-Endian Format gesendet werden, also jeweils erst das Lowbyte (GGGGRRRR) und dann das Highbyte (----BBBB).

Hier ein Beispiel, um alle Farben der Palette zu setzen:

  SetPalette:
  ld  hl, $c000
  rst $10
  
  ld c, $be
  ld b, 64
  ld hl, Palette
  otir
  ret
  
  Palette:
  .incbin "palette"

Tilemap und Tiles

Die Tilemap hat im Modus M4 eine Größe von 32x28 Tiles. Davon angezeigt werden 20x18 Tiles. Mit den VDP-Registern 8 und 9 kann man bestimmen, welcher Ausschnitt angezeigt wird.

Das Format eines Tiles in der Tilemap ist:

  MSB          LSB
  ---pcvhnnnnnnnnn
  
  p: Priorität           - Das Tile wird vor den Sprites dargestellt (Farbe 0 bzw. 16 ist transparent)
  c: Palette             - Die Farben 16 bis 31 statt 0 bis 15 werden benutzt
  v: Vertikal spiegeln   - Das Tile wird vertikal gespiegelt
  h: Horizontal spiegeln - Das Tile wird horizontal gespiegelt
  n: Tilenummer          - Die Nummer des Tiles (0 bis 511)
  • VDP Register 8

Legt das horizontale Scrolling fest. Die X-Koordinate des Tilemap-Ausschnitts ist (48 - vdp_reg8). Dieses Register läßt sich in jeder Zeile ändern.

  • VDP Register 9

Legt das vertikale Scrolling fest. Die Y-Koordinate des Tilemap-Ausschnitts ist (24 + vdp_reg9). Man kann dieses Register leider nur einmal pro Frame ändern.

Hat man diese beiden VDP-Register auf 0 gesetzt, wird also nicht der linke obere Teil der Tilemap angezeigt. Will man diesen anzeigen, setzt man VDP Register 8 auf 48, und VDP Register 9 auf -24.

  • VDP Register 2

VDP Register 2 legt die Adresse der Tilemap fest. Die Bits 1 bis 3 dieses Registers werden um 10 Bits nach links geschoben. Die Adresse der Tilemap ist also ((vdp_reg2 & $0E) << 10). Man kann dieses Register mehrmals pro Frame ändern.

Meistens setzt man VDP Register 2 auf $FF, dann ist die Adresse der Tilemap $3800.

Sprites

Der VDP im Game Gear kann bis zu 64 Sprites auf dem Screen darstellen. Ändert man die Adresse der Sprite-Tabelle (siehe VDP-Register 5), können es sogar noch mehr sein. Allerdings können immer nur 8 davon pro Zeile dargestellt werden.

  • VDP-Register 5

VDP-Register 5 bestimmt die Adresse der Sprite-Tabelle im VRAM. Die Bits 1 bis 6 werden um 7 Bits nach links geschoben. Die Adresse der Sprite-Tabelle ist also ((vdp_reg5 & 0x7E) << 7).

Meistens wird VDP-Register 5 auf $FF gesetzt, dann ist die Adresse der Sprite-Tabelle $3F00.

  • VDP-Register 6

Die Sprites können 256 Tiles benutzen. Ob die Tiles 0 bis 255, oder 256 bis 511 benutzt werden, wird mit VDP-Register 6 bestimmt. Ist Bit 2 gesetzt, werden die Tiles 256 bis 511 benutzt, ansonsten 0 bis 255.

  • Format der Sprite-Tabelle

Die Sprite-Tabelle ist in zwei Teile aufgeteilt: Ein Teil für die Y-Koordinaten und ein Teil für die X-Koordinaten und die Tile-Nummern.

Die Offsets für die Sprite-Daten von Sprite i sind: i: Y-Koordinate 128 + 2 * i: X-Koordinate 129 + 2 * i: Tile-Nummer

In den ersten 64 Bytes sind also die Y-Koordinaten, und ab Offset 128 gibt es jeweils ein Byte-Paar mit X-Koordinate und Tile-Nummer. Direkt hinter den Y-Koordinaten ist ein 64 Byte großer Speicherbereich, der von der Sprite-Tabelle nicht benutzt wird. In ihm kann man z.B. 2 Tiles speichern.

Um ein Sprite in der linken oberen Ecke des Bildschirms anzuzeigen, setzt man die X-Koordinate auf 48 und die Y-Koordinate auf 24.

Bei Sprites, die nicht sichtbar sein sollen, kann man z.B. die Y-Koordinate auf 0 setzen. Eine andere Möglichkeit ist, die Y-Koordinate eines Sprites auf 208 zu setzen. Dieser spezielle Code signalisiert das Ende der Sprite-Tabelle, so daß dieses Sprite selbst und alle folgenden Sprites nicht mehr angezeigt werden.

Display Timing

Interrupts

Der VDP kann Frame- und Line-Interrupts erzeugen.

Sound

Übersicht über den PSG

Der PSG im Game Gear bietet:

  • 3 Kanäle mit eine Rechteckswelle
  • 1 Geräuschkanal