Das zweite Level - Kompatible Spieleprogrammierung, Teil 2: Grundregeln zur hardwarenahen Programmierung

Teil 1 Richtlinien und Blick auf andere Rechnersysteme
Teil 2 Grundregeln zur hardwarenahen Programmierung
Teil 3 Kompatible Spiele unter MultiTOS
Teil 4 Spiele auf Grafikkarten: alternative Treiberkonzepte

Im zweiten Teil unserer Artikelserie wollen wir auf den besonders kritischen Teil der direkten Hardware-Programmierung eingehen. In bestimmten Situationen kann man dem Spieleprogrammierer nicht einmal verübeln, direkt auf die Hardware zuzugreifen, weil uns das Betriebssystem (das wir alle so lieben) keine Möglichkeit zu Verfügung stellt, die gewünschte Funktion auszuführen. Der direkte Zugriff sollte aber nicht Philosophie, sondern lediglich eine Notlösung bleiben.

Der Unterschied zwischen Demos und Spielen: Demos sind Programme, welche von Codem (Programmierern), Musikern und Grafikern gestaltet worden sind. Sie sind oft künstlerisch, und ihre Macher benutzen Tricks und undokumentierte Verfahren, um ihre Ideen mit größtmöglicher Wirkung umzusetzen. Ich finde, daß dies eine Art von Kunst ist, ähnlich wie die bekannten Graffities. Auch ähnlich wie bei Graffities, sind die Demos wohl erst im kriminellen Bereich entstanden, so haben Cracker (Coder, welche den Kopierschutz aus den Programmen entfernen) „geknackte“ Programme, aus Geltungssucht, mit Erkennungszeichen versehen, z.B. um zu zeigen, wer den Kopierschutz wann aus dem Programm entfernt hat. Diese Erkennungszeichen wurden immer auffälliger und kunstvoller, und da einige dieser Cracker irgendwann einmal selbst mit dem Programmieren von Spielen ihr Geld verdienten, schrieben sie Intros bzw. Demos nicht mehr, um sie vor die geknackten Spiele zu setzen, sondern um zu zeigen, was auf ihrem Rechner möglich ist. Das ermöglichte es ihnen, sich von Programmierern auf anderen Rechnern abzugrenzen. Alles in allem sind Demos als Kunstwerke zu sehen und müssen daher auch nicht konform zu irgend etwas sein!

Spiele dagegen werden geschrieben, um zu unterhalten und/oder intellektuelle und soziale Fähigkeiten zu trainieren (oder so ähnlich), aber natürlich auch, damit der Programmierer sich neue Hardware leisten kann. Natürlich können auch Spiele Kunstwerke sein - sowohl die Grafik als auch die Musik des Spiels werden vom Gesetz ja als urheberrechtlich schützenswert erachtet -, aber sie verrichten zu allererst, wie auch alle anderen Applikationen, eine Dienstleistung für den Benutzer und sollten daher so systemkonform wie irgend möglich geschrieben werden. Das sollten endlich auch Programmierer verstehen, die - wie ich -, zu 8-Bit-Homecomputer-Zeiten das Programmieren begonnen haben.

Probleme und Fehlerquellen

Ich möchte hierbei einige Fehler von Programmierern aufzeigen, die häufig zu irgendwelchen Inkompatibilitäten mit vorhandener Hardware geführt haben.

Es wird „versehentlich hinter den Bildschirmspeicher geschrieben“. Abgesehen davon, daß alle Zugriffe auf den Bildschirm durch das Betriebssystem erfolgen sollten (was, wie wir sehen werden, aus „ästhetischen Gründen“ nicht immer so einfach möglich ist), bringt der Zugriff auf Stellen, an denen kein Speicher liegt, immer dann Probleme, wenn diese, durch die von der MMU (wie bei STE/TT) überhaupt nicht abgefangen werden. Da die ATARI-ST/STE-MMU (bei STE-MCU-Mischung aus MMU und GLUE-Baustein) nur 22 Adreßleitungen hat, können auch nur 222 Adressen dekodiert werden. Bei Adressen, die größer als 4 MB sind, greift der Prozessor quasi ins Leere und erzeugt einen Busfehler. Auf Adressen bis zu 4 MB liefert die ATARI-MMU dem Prozessor die entsprechenden Signale, auch wenn kein RAM vorhanden ist. Aus diesem Grund, und weil es einfach zum sauberen Programmierstil gehört, nur wirklich vorhandene Adressen zu beschreiben, sollte man nicht versuchen, Adressen hinter dem Bildschirmspeicher zu beschreiben.

Funktion 1.Byte+Midi Kanal 2.Byte (7 Bit benutzt) 3.Byte (7 Bit benutzt)
  Betehlsbute
Note an $90 Nummer der Note Anschlagsdynamik, 0=Note aus
Pitch Bend $E0 Pitch Bend Daten, niederw. Pitch Bend Daten, höherw.
Proqramm Chanae $C0 Klananummer keins
Mono. Aftertouch $D0 Intensität keins
Polq. Aftertouch $A0 Nummer der Note Intensität
 
Common Commands
Nicht definiert $F1, $F4, $F5
Song Position $F2 niederw. Position höherw. Position
Song Select $F3 Nummer des Songs keins
Tune Reauest $F6 keins keins
System Reset $FF keins keins
 
Real Time Inform.
Timing Clock $F8 keins keins
Nicht definiert $F9. $FE keins keins
Sonq Pos. START $FA keins keins
Continue $FB keins keins
Stop $FC keins keins
Active Sensinq $FE keins keins
 
Control Chanqes immer $80 Befehlsbyte
Modulationsrad $1 Intensität
Breath Controller $2 Intensität
Fuß Pedal $4 Intensität
Portamento Time $5 Intensität
Data Entry $6 je nach Funktion
Gesamt Lautstärke $7 $0 keine bis $7F Laut
Hold Sustain Pedal $40 $0 = AUS, $7F= AN
Portamento $41 $0 =AUS, $7F=AN
Suste no Pedal $42 $0 =AUS, $7F=AN
Soft Pedal $43 $0 =AUS, $7F=AN
Channel Mode Local Control $7A $0 =AUS, $7F=AN
All Notes Off $7B $0
OMNI MODE OFF $7C $0
OMNI MODE ON $7D $0
MONO ON/POLYOFF $7E Anzahl der MIDI Kanäle
POLY ON/MONO OFF $7D $0
System Exclusive init. String Hersteller Midi Kanal Daten Ende
$F0, Sys. Excl. $F7, Keine Real Time Daten z.B. $23 Roland $41 Yamaha $0 bis $F Hersteller Abhängig $F7

Tabelle mit einer Übersicht zum MIDI-Befehlssatz

Das TOS-Problem

Aus irgendwelchen Gründen haben einige Spieleprogrammierer das dringende Bedürfnis, die Versionsnummer des TOS zu erfahren, und greifen dabei direkt auf die ROMs, z.B. bei Adresse $FC0000, zu. Nun liegt das Betriebssystem bei STE/TT/Falcon auf $E00000 und gelegentlich sogar irgendwo im RAM. Viel sauberer ist es über _sysbase, eine Systemvariable, welche - im Normalfall - auf die Anfangsadresse des Betriebssystems zeigt, an die Versionsnummer, welche im zweiten Wort steht, zu kommen. Wer sauber programmieren will, benötigt maximal die Cookies, GEMDOS- und AES-Versionsnummer.

Das Beschreiben von reservierten Bits und Bytes

Bei einigen Spielen wird, damit das Beschreiben des Bildschirmspeichers synchron zum Bildaufbau geschehen kann, die Bildwiederholfrequenz mit einem move.b #0,$ffff820a.w von 50 Hz auf 60 Hz umgeschaltet. Dieses bereitet auf dem TT Schwierigkeiten, da dort das erste Bit des Registers invertiert ist und sich beim TT die Bildwiederholfrequenz gar nicht auf 50 Hz wechseln läßt. Das bedeutet, daß jeder Versuch, auf diese Weise die Bildwiederholfrequenz zu wechseln, auf dem TT einen schwarzen Bildschirm erzeugt, worauf die VBL-Routinen nicht mehr ausgeführt werden. Da hilft dann nur noch ein Reset. Es gibt für den TT einen allgemeinen Patch, der den TIMER A des zweiten MFPs so programmiert, daß er dieses Bit immer wieder zurücksetzt; aber besser ist es, im Programm mit andi.b#%11111101, $ffff820a.w das Bit zu löschen (60 Hz) und mit ori #2,$ffff820a.w das Bit wieder zu setzen (50 Hz). Das gleiche Problem tritt auf, wenn jemand das „DMA-Control-Register“ als komplettes Wort anspricht, da sich im höherwertigen Byte, welches in STE/TT unbenutzt war, die Interrupts, die - wenn der DMA-Kanal das Ende des Frame-Buffers erreicht - auftreten, an- bzw. abschalten lassen. Selbst wenn man diese Interrupts dann per Betriebssystem setinterrupt (XBIOS 135) setzt, aber doch nach STE/TT-Manier auf das komplette Register zugreift, schaltet man die Interrupts wieder aus.

Ich will damit nur aufzeigen: wenn man schon auf die Hardware direkt zugreift, sollte man wirklich versuchen, nur die Bits zu beschreiben, die man auch wirklich ändern möchte.

Undokumentierte Hardware-Register...

... sollte man in Spielen schon gar nicht verwenden, auch nicht die Register des Falcon-Sound-Subsystems (außer die STE-kompatiblen), und schon ganz und gar nicht das Interface zum DSP. Das ist Betriebssystemerweiterungen wie den diversen Grafikerweiterungen, Systemmonitoren und natürlich Demoprogrammierern Vorbehalten.

Kopierschutz

Große Probleme, gerade bei der Kompatibilität von Spielen, bereitet der Kopierschutz. Bei kopiergeschützten Disketten fragen meist mehr oder weniger zeitkritische Routinen die entsprechenden Sektoren der Diskette ab und überprüfen z.B. die Abstände, welche diese zueinander haben. Wie auch immer, zum einen kann ein Laufwerk, welches leicht neben den Normeinstellungen liegt, schon daran schuld sein, wenn das Spiel behauptet, es wäre nicht von der Originaldiskette geladen worden. Zum anderen kann man ein so geschütztes Programm in der Regel nicht auf Platte installieren (siehe Teil 1, Punkt [A] der Game/Entertainment Guidelines). Klar, niemand möchte dauernd irgendein Password von „Seite XY, Absatz AB und Wort Nr. N“ eingeben, aber gegen einen Paßwortschutz bei der Installation auf Platte - und dann nie mehr - kann keiner etwas sagen. Ein einmal installiertes Programm kann normalerweise natürlich auf Disketten kopiert - Backup - und dann auf einem anderen Rechner wieder auf die Platte kopiert werden. Nun geht das aber nur so lange, wie das Programm diesen Transfer nicht bemerkt. Wenn aber das Spiel bei der Installation die Konfiguration des Rechners z.B. Rechnertyp, Plattentyp, Plattengröße, Partitionierung, TOS-Versionsnummer, Speicherausbau; FPU (vorhanden oder nicht) usw. in einer Datei oder im Programm gut (!!!) kodiert ablegt und beim Start von Platte immer wieder testet, kann man ziemlich sicher sein, daß ein Spiel immer mit Handbuch, farbiger Kopierschutzkarte oder ähnlichem kopiert werden muß. Das schränkt unter Umständen den Kreis der kopierwilligen Interessenten ein und der- oder diejenige kauft das Spiel lieber, als sich die Arbeit mit dem Kopieren zu machen. Aber es gibt sicherlich noch andere Möglichkeiten, den Kopierschutz anwenderfreundlich zu gestalten.

Raster-Interrupts...

... erzeugen eine Unterbrechung des Programms, wenn eine bestimmte Rasterzeile erreicht wurde. Es ist dann z.B. möglich, mit einer im Interrupt ausgeführten Routine alle oder einzelne Farben der Palette zu ändern (je nachdem, wie häufig der Interrupt auftritt), und das erlaubtes, z.B. in der „ST-niedrig“-Auflösung, mehr als nur sechzehn Farben gleichzeitig auf den Bildschirm zu zaubern (siehe dazu auch [2]). Allerdings sollte man vermeiden, Programme zu schreiben, welche die Umschaltung nicht via Interrupt, sondern durch Synchronisation mit dem Bildaufbau (Zeichnen des Hintergrunds, Sprites etc.) oder ähnlichen von der Prozessorgeschwindigkeit abhängigen Techniken vornehmen. Natürlich sind Raster-Interrupts alles andere als hardwareunabhängig (s. Teil 1, Game/Entertainment Guidelines, [G] und [H]), und es kann zur selben Zeit immer nur ein Programm diese Technik anwenden. Es ist aber durchaus möglich, Spiele zu schreiben, welche auf dem ST/STE mit Raster-Interrupts arbeiten und auf TT/Falcon die erweiterte Hardware, d.h. mehr Farben etc., ausnutzen.

Abspielen digitalisierter Sounds

Ohne direkten Zugriff auf Hardware-Register ist nur auf dem Falcon030 das Abspielen von digitalisierten Sounds möglich. Auf allen anderen ATARI-Rechnern muß man die Hardware direkt programmieren.

Der PSG (Programmable Sound Generator), welcher im Moment noch in jedem ATARI Verwendung findet, hat - zumindest auf den ST/STE/TT Rechnern - eine undokumentierte Besonderheit: die zwei Register, mit welchen man ihn anspricht, liegen mehrmals gespiegelt hintereinander. Diese Eigenheit wurde auf dem Falcon nicht mehr implementiert.

Die beiden PSG-Register lassen sich zwar über das Betriebssystem programmieren, das Abspielen von digitalisierten Klängen läßt sich darüber aber nicht realisieren, da man aus Zeitgründen die Hardware direkt ansprechen muß. Somit läßt sich hier der direkte Zugriff auf die Hardware nicht vermeiden.

Das DMA-Sound-Subsystem von STE/TT wird vom Betriebssystem ebenfalls nicht unterstützt. Auch hier ist die Programmierung nur durch den direkten Zugriff auf die Hardware möglich. Der Falcon-DMA-Sound ist weitgehend kompatibel zu dem von STE/TT, wenn man (wie schon erwähnt) nur die dokumentierten Bits beschreibt. Mein Vorschlag an den Spieleprogrammierer wäre, im Programm per _5M)-Cookie zu testen, welche Soundmöglichkeiten der Rechner bietet und dem User eine Auswahl der möglichen Klangerzeuger zu bieten, die es auch ermöglicht, den Sound einfach abzuschalten. Alternativ könnten natürlich auch Klangerzeuger, wie diverse Drucker-/ROM-Port-D/A-Wandler unterstützt werden.

Auf dem Falcon030 wird der Sound des PSG „nur noch“ über den CODEC (A/D-D/A-Wandler) in das System eingebunden. Es ist schon abzusehen, daß es TOS-kompatible Rechner geben wird, welche den PSG nicht mehr unterstützen. Auch der direkte Zugriff auf die STE/TT-kompatible Hardware ist als bedenklich anzusehen. Daher kann es natürlich sein, daß es irgendwann ATARI-Rechner geben wird, die keine der im Moment vorhandenen Arten der Sounderzeugung unterstützen. Leider gibt es auf dem ATARI noch kein Treiberkonzept (siehe z.B. Windows, Unix), welches es ermöglichen würde, hardwareunabhängig auf unterschiedliche Sounderzeuger zuzugreifen; aber darüber im 4. Teil (Alternative Treiberkonzepte) mehr.

Ach ja, bevor ich es vergesse, gerade bei MOD-Playem und ähnlichen Abspielprogrammen ist es recht einfach, die Noten in MIDI(Musical Instruments Digital Inter-face)-Daten umzuwandeln (s. Tabelle).

Was man noch so vermeiden sollte!

Das Betriebssystem bietet ausreichende Möglichkeiten, IKBD, DSP, MFP, WD 1772 bzw. AJAX zu programmieren, so daß es wirklich nicht nötig ist, diese Chips per Hand zu programmieren. Die direkte Hardware-Programmierung ist auf jeden Fall den systemnahen Programmen wie Device-Treibern und ähnlichem Vorbehalten. Seit es TOS-kompatible Rechner wie Medusa oder die Janus-Erweiterung für PCs gibt, sollte jedem Programmierer klar sein, daß nicht mehr in jedem Rechner, auf dem TOS läuft, auch nur annähernd die gleichen Hardware-Komponenten vorhanden sein müssen. Selbst wenn die TOS Ikbd-Routine, welche die Pakete des ACIA 6850 auswertet, - bei zu vielen Interrupts -Probleme mit dem Auswerten der Maus hat, sollte man keine neue Ikbd-Routine schreiben, sondern eher einen Weg suchen, den einen oder anderen Interrupt zu vermeiden oder die Interrupt-Routine zu verkürzen. Der Kreativität des Programmierers sind da keine Grenzen gesetzt.

Vektoren und ähnliches

Von allen Vektoren, die man in Programmen (auch Spielen!) so verbiegt, also auf eigene Routinen zeigen läßt, sollte man vor dem Ändern den Wert retten und diesen vor der Beendigung des Programms zurückschreiben. Zuwiderhandlungen können zu mysteriösen(!) Abstürzen führen. Ebenfalls retten und zurückschreiben sollte man die Farbpalette (siehe Teil 1 [D]), „weil es echt nervig“ ist, auf dem Desktop z.B. mit rosa Fenstern oder schwarzer Schrift auf schwarzem Grund zu arbeiten. Außerdem sollte man Vektoren - falls möglich - mit Betriebssystemroutinen verbiegen, retten und zurückschreiben. Das ist keine Willkür, denn ab 68010 existiert zusätzlich ein Vektorbasisregister (VBR), welches es ermöglicht, verschiedene Vektortabellen im Speicher zu halten. Es ist also ab 68010 nicht sicher, ob die Vektortabelle, welche Vektoren zur Fehlerbehandlung, LINE F, LINE A, RESET und TRAP-Exceptions etc. enthält, wie beim 68000er bei Adresse $0 beginnt.

Ein entsprechend erweitertes Betriebssystem würde diese Veränderung natürlich unterstützen, was im Moment noch nicht der Fall, aber auch nicht nötig ist. Daher sollte man Setexc() benutzen, um die gewünschten Vektoren zu verbiegen, was zusätzlich den Vorteil hat, daß man vorher den Prozessor nicht in den Supervisor-Mode schalten muß (siehe dazu auch [12] und [13]).

Exceptions und Interrupts

Beim Auftreten von Exceptions und Interrupts werden vom Prozessor Informationen auf den Stack gelegt, welche für die Wiederaufnahme des normalen Programmablaufs nötig sind. Im Normalfall hat ein Programmierer damit nicht viel zu tun, da man selten in die Verlegenheit kommt, eigene Trap-Dispatcher, FPU-Simulatoren oder ähnliches zu schreiben. Allerdings scheint es doch einige Programmierer (gerade die von Spielen) zu geben, die sich ihr eigenes kleines Betriebssystem schreiben und eigene Trap-Dispatcher, LINE-A-Funktionen oder ähnliches installieren. Solche tiefen Eingriffe ins System sind an sich schon nicht zu empfehlen, weil so etwas das Terrain von Betriebssystemen und Systemerweiterungen ist. Wer allerdings nicht beachtet, daß ab 68010 aufwärts das Stack-Format um mindestens ein Wort erweitert wurde, welches dann Vektor-, Offset- und Format-Code enthält, kann sowieso nicht erwarten, daß sein Spiel auf Prozessoren größer als 68000 läuft.

Der Format-Code wurde von Motorola festgelegt und gibt an, wieviel Wörter der Prozessor auf den Stack gelegt hat, da die neueren Prozessoren der 68000er-Familie Exceptions mit unterschiedlichen Stack-Frames erzeugen. Eine Möglichkeit, um zu testen ob das Programm auf einem Rechner mit erweitertem Stack-Format läuft, ist die System variable _longframe, die man an der Adresse $59e findet. Wenn deren Inhalt >0 ist, benutzt der Prozessor das erweiterte Stack-Format (siehe [12]).

Geschwindigkeitsprobleme

Selbst wenn man Spiele auf einem TT, Falcon030 oder diversen Beschleuniger-Boards zum Laufen gebracht hat, sind diese meist unspielbar. Die Spiele werden so abgearbeitet, „als hätte jemand den schnellen Sichtvorlauf gedrückt“. Ähnlich interessant wirken Musikstücke von Programmen, deren Abspielroutinen mittels des VBL-Interrupts aufgerufen werden. Der VBL tritt immer dann auf, wenn der Elektronenstrahl vom Bildschirmende wieder zum Bildschirmanfang wandert. Wie oft das in der Sekunde passiert, hängt von der Bildwiederholfrequenz ab. Die ist bekanntlich keine Konstante und läßt sich je nach ATARI auf bis zu 100 Hz einstellen. So wirken Musikstücke, deren Abspielroutinen für 50 Hz konzipiert sind, auf 100 Hz natürlich doppelt so schnell.

Um dies zu vermeiden, kann man einen dieser „wunderbaren Bausteine“, die uns von ATARI in „wunderbarer“ Voraussicht gegeben wurden, nutzen. Der MFP stellt Timer zur Verfügung; Timer A und B werden vom System nicht genutzt und sind daher frei verfügbar. Wer schon mit Timern gearbeitet hat, weiß, daß sie relativ frei programmierbar sind und zu einem festen Zeitpunkt oder nach einer bestimmten Menge von Ereignissen einen Interrupt auslösen können. Ersteres ist ideal, um Samples abzuspielen oder als Takt für ein Spiel zu dienen, letztgenanntes ist ideal für Raster-Interrupts und DMA-Sound.

Übrigens, das DMA-Soundsubsystem erzeugt einen Interrupt, wenn ein Frame (Teil eines digitalisierten Klangs) bis zu Ende abgespielt wurde. Wählt man die Länge des Frames richtig, kann man auch gleich den Sequenzer in die Interrupt-Routine einbauen und unter Umständen noch das Spiel mit der Realzeit synchronisieren. Wem das alles zu umständlich ist, der sollte zumindest versuchen, im Programm die Zeit von Leerschleife und zeitintensiven Funktionen (z.B. Sprites, Kollision, Scrolling etc.) zu messen und Leerschleifen an günstigen(!) - bitte nicht so, daß man Sekunden auf die Reaktion des Joysticks warten muß - Stellen im Programm einzubauen, um den Rechner je nach Leistung der Plattform mehr oder weniger zu beschäftigen. Idealerweise versucht man, wenn man „mehr Rechenzeit als benötigt“ hat, Sound mit höherer Abspielfrequenz und Animationen mit flüssigeren Bewegungen ablaufen zu lassen oder besser noch mehr und/oder größere Sprites mit mehr Farben in einer anderen Grafikauflösung zu realisieren.

Literatur:

[1] Thomas Binder, Gamefix, Falcon030 wird ST(E)-kompatibel, ST-Computer 11/93

[2] Klaus-Dieter Pollack, Spiele selbst programmieren, erschienen zur CeBit 1993 im Heim Verlag, ISBN 3-928480-13-8

[3] Falcon030 Developer Documentation, ATARI Oktober 1992

[4] Jorgo Schimanski, Spieleprogrammierung in Assembler auf dem Amiga, erschienen 1991 im Heim Verlag, ISBN 3-928480-01-4

[5] RISC OS, Programmers Reference Manual Acorn Computer Limited 1989, ISBN 1-86250-060-3

[6] Archimedes (Sonderheft der 64er), erschienen im Markt und Technik Verlag

[7] Two Tribes, ein schachähnliches Action-/Strategiespiel, welches voraussichtlich irgendwann 1995 erhältlich sein wird und auf allen TOS-kompatiblen Rechnern laufen wird.

[8] Jankowski, Rabich, Reschke, ATARI ST-STE-TT Profibuch, erschienen 1991 im Sybex Verlag, ISBN 3-88745-888-5

[9] Martin Huber, Nova Treiber ab Vers. 2.0

[10] Sven und Wilfried Behne, NVDI ab Vers. 2.5

[11] Sven und Wilfried Behne, Enhancer ver. 1.0

[12] Steve Williams, 68030 Assembly Language Reference, erschienen 1989 imAdisson Wesley Verlag, ISBN 0-201-08876-2

[13] MC 68040 User’s Manual, Motorola 1989

[14] James F. Foley, van Dam, Feiner, Huges, Computer grafics: principles and practice, Adisson Wesley, ISBN0-201-12110-7

[15] Gerd Respondek, Farblose Darstellung, CT 11/91


Klaus-Dieter Pollack
Aus: ST-Computer 12 / 1994, Seite 86

Links

Copyright-Bestimmungen: siehe Über diese Seite