CONTENTS | PREV | NEXT
2. Programmierung des PowerPC mit WarpOS (von Steffen Häuser)
»Seit dem Erscheinen der PowerUP-Karten erschienen auf dem Aminet bereits
einige Programme, die den PPC unterstützen. Es gibt jedoch zahlreiche
Autoren, die ihre Programme noch nicht mit PPC-Support versehen, obwohl
dies durch die Verwendung von StormC und WarpOS eigentlich sehr einfach
ist. Dieser Artikel beschreibt, wie man ein Programm mit PPC-Support
versieht.
Dabei wäre es sogar durchaus denkbar, ein Programm jetzt für den 68k zu
entwickeln - da weder PPC-Karte noch PPC-Compiler verfügbar -, jedoch mit
Blick auf eine leichte PPC-Portierbarkeit, und dann später die wenigen noch
nötigen Schritte, um eine PPC-Version zu generieren.
Dieses Vorgehen dürfte vor allem für Spiele-Entwickler interessant sein. Im
Zweifelsfall, falls ein Rechner gesucht wird, auf dem ein bereits
fertiggestelltes PPC-Programm compiliert werden kann, ohne gleich eine PPC-
Karte kaufen zu müssen, stehe ich auch gerne zum Recompilieren zur
Verfügung (obwohl es sicher praktikabler wäre, einen eigenen Rechner hier
zu haben). Das Programmtesting kann im Übrigen prinzipiell auch mit der
68k-Version erfolgen, da eigentlich keine größeren Änderungen erforderlich
sind.
Prinzipiell erfolgt die Anpassung in folgenden Phasen (um die Entwicklung
zu vereinfachen, ist empfohlen, dies schon während der Entwicklung der 68k-
Version durchzuführen):
A) Umschreiben aller 68k-Assembler-Teile nach C oder C++ oder PPC-ASM
B) ANSI/StormC-Anpassung
C) PPC-Anpassung
D) Kontextswitch-Optimierung
E) Weitere Anpassung
Im Gegensatz zu einer PPC-Anpassung auf ppc.library hat man bei WarpOS nach
Schritt A) eigentlich schon fast "gewonnen", die letztlichen PPC-
Anpassungen sind (zumindest bei Sourcen ohne Assembler-Optimierung)
minimal. Ich verzichte darauf, auf Teil A) einzugehen, wer unbedingt
Assembler-Projekte auf PPC portieren möchte, sollte E-Mail-Kontakt mit mir
aufnehmen, da es evtl. ein besser geeignetes Verfahren der Portierung als
das hier beschriebene gibt (und zudem vor einer Riesenarbeit geprüft werden
sollte, ob es sich überhaupt lohnt).
Ich empfehle, zumindest Schritt B) bereits während der Entwicklung der 68k-
Version vorzunehmen, um später nicht mehr durch den ganzen Source
durchgehen zu müssen.
B. StormC - ein strenger ANSI-Compiler
--------------------------------------
Der wildeste Part bei der Anpassung eines PPC-Programms ist nicht etwa die
Anpassung von 68k auf PPC, sondern von SAS/C oder GNU C auf StormC.
StormC ist ein strenger ANSI-Compiler, daher sind Standard-C-Funktionen,
die im ANSI-Standard nicht verfügbar sind, nicht zulässig. Einige der
Funktionen können über die offiziell noch nicht releaste UnixLib emuliert
werden.
Die Liste der nicht erlaubten SAS/C-Funktionen wäre (keine dieser
Funktionen ist im ANSI-Standard enthalten...):
astcsma isascii iscsym iscsymf toascii scdir stcpm
stcpma stcsma stccpy stpcpy stcis stcisn stclen
stpbrk stpchr stpchrn strcmpi strnset
strset stcarg stpsym stptok stpblk strbpl strdup
strins strmid stcd_i stcd_l ecvt fcvt gcvt
stch_i stch_l stci_d stci_h stci_o stcl_d stcl_h
stcl_o stco_i stco_l stcu_d stcul_d toascii stpdate
stptime __datecvt __timecvt utpack utunpk cot iabs
max min pow2 __emit getreg putreg geta
isatty ovlyMgr dqsort fqsort lqsort sqsort strsrt
tqsort drand48 erand48 jrand8 lcong48 lrand48 mrand8
nrand48 seed48 srand48 __autoopenfail chkabort Chk_Abort
_CXBRK __exit onexit _XCEXIT forkl forkv onbreak
wait waitm bldmem rstmem sizmem chkml getmem
getml halloc lsbrk sbrk _MemCleanup rbrk rlsmem
rlsml memccpy movmem repmem setmem swmem except
__matherr poserr datecmp timer __tzset getch fgetchar
fputchar _dread _dwrite read write clrerr close
_dclose fcloseall creat _dcreat _dcreatx fdopen fileno
fmode iomode open _dopen flushall mkstemp mktemp
setnbf _dseek lseek tell access chkufb chmod
fstat getfa getft stat stcgfe stcgfn stcgfp
strmfe strmfn strmfp strsfn unlink argopt chgclk
dos_packet getclk getasn getdfs putenv rawcon stackavail
stacksize stackused chdir closedir dfind dnext findpath
getcd getcwd getfnl getpath mkdir opendir readdir
rmdir seekdir rewinddir telldir readlocale scr_beep scr_bs
scr_cdelete scr_cinsert scr_clear scr_cr scr_curs scr_cursrt scr_cursup
scr_eol scl_home scr_ldelete scr_lf scr_linsert scr_tab _CXFERR
_CXOVF _EPILOG _PROLOG
Die Liste sieht lang aus, aber man muß bedenken, daß die meisten
aufgelisteten Funktionen "extrem exotische Funktionen" sind, die vermutlich
vielen Programmierern gar nicht bekannt sind. Im ANSI-Standard sind sie
jedenfalls nicht enthalten.
Nach den Ersetzungen (die wichtigsten Ersetzungen laufen auf das Ersetzen
von open/close/read/write hinaus...) gibt es noch einen anderen Aspekt von
ANSI zu bedenken:
Schreibweisen wie:
char *string=malloc(300);
die bei SAS/C nur eine WARNING produzieren, produzieren bei strengen ANSI-
C-Compilern einen Fehler. Derartiger Code sollte so aussehen:
char *string=(char *)malloc(300);
ANSI C verlangt eine STRENGE TYPISIERUNG. Dies gilt übrigens auch für
Zeiger auf Funktionen. Ein guter "Trick", um einen nicht ANSI-konformen
Source rasch zu konvertieren, ist:
1. Einfach mal durchcompilieren und dabei auf die Warnings/Fehler achten.
2. Alles, was nach Zeiger aussieht, und nicht streng typisiert ist, auf
void * casten. Alles, was nicht nach Zeiger aussieht, und falsch
typisiert ist, je nach Anwendung auf int, long oder double casten.
Zeiger auf Funktionen auf void * casten, z.B.:
void *funktion=(void *)meine_funktion;
Programmierer, die keinen StormC besitzen, können das "Test-Compilieren"
auch mit SAS/C im "STRICT_ANSI"-Modus machen, der meines Wissens ziemlich
ähnlich wie StormC reagiert.
Zudem sollte man noch jedes Auftreten von K&R-Syntax durch die normale C-
Syntax ersetzen, z.B.
void main(argv,argc)
int argv;
char **argc;
durch
void main(int argv,char **argc);
ersetzen, da der PPC-Compiler keine K&R-Syntax mag.
Falls ein Source - wie etwa der Doom-Source - relativ häufig Funktionen aus
der Unix-Welt verwendet, so hat es sich als hilfreich erwiesen, eine auf
StormC umgeschriebene Version der AmiTCP/IP-Includes zu verwenden, so daß
Datentypen wie z.B. dev_t zur Verfügung stehen. Ein Source, der von Anfang
an auf ANSI-Kompatibilität getrimmt ist, sollte solche Typen jedoch nicht
nötig haben. Wer dies dennoch nötig hat, soll einfach mal bei mir mit Bitte
um Hilfestellung anfragen (Kontaktadresse siehe unten).
Ein letzter Aspekt der Compiler-Unterschiede wären spezifische Unterschiede
zwischen SAS/C und StormC. Ich habe mich hierbei auf die Beschreibung
"normaler" Sources beschränkt, wer gerne PPC-Shared Libraries machen
möchte, soll mich kontaktieren, und ich werde auch hier beschreiben, was
die Unterschiede zwischen SAS/C und StormC sind.
Einige Schlüsselworte von SAS/C entfallen einfach, indem man sie auf "leer"
definiert:
#define __stdargs
#define __regargs
#define __asm
__far sollte durch das Schlüsselwort FAR ersetzt werden, __inline durch
inline, in diesen Dingen orientiert sich StormC ebenfalls mehr an ANSI.
__chip, __fast und __interrupt funktionieren nicht so wie bei SAS/GNU, hier
muß man den Weg über die entsprechenden OS-Funktionen gehen.
Registerparameter funktionieren (bis auf das Weglassen des Schlüsselwortes
__asm) genau wie beim SAS/C, jedoch sollte man für eine PPC-Version darauf
verzichten, da der PPC ja z.B. kein Register d0 hat (das ist ein 68k-
Register). Man kann natürlich
register int a;
schreiben, das funktioniert. Der PPC sucht dann selbst nach einem
geeigneten Register, bzw. der Compiler tut das.
C. PPC-Anpassung
----------------
So, den wildesten Part haben wir nun, es folgen eigentlich nur noch
Kleinigkeiten.
Da die eigentliche PPC-Anpassung ja noch gar nicht erfolgte (bis auf die
Kleinigkeit mit den Registern...), folgt, daß man mit WarpOS die 68k- und
die PPC-Version sehr leicht parallel entwickeln kann.
Der erste wesentliche Unterschied betrifft die OS-Includes. Ein
#include <proto/exec.h>
oder
#include <clib/exec_protos.h>
#include <pragma/exec_protos.h>
schreibt sich unter PPC als:
#include <clib/exec_protos.h>
Die Pragmas entfallen komplett, und man sollte auch keine Protos includen.
Für die Parallelentwicklung sieht das Ganze so aus:
#include <clib/exec_protos.h>
#ifndef __PPC__
#include <pragma/exec_lib.h>
#endif
Der Define __PPC__ ist immer entsprechend der Compiler-Optionen gesetzt.
Ein weiterer Unterschied fällt bei der Verwendung von Subtasks an. In
dieser einfachen Variante der PPC-Portierung verwenden wir ja keine Mixed
Binaries (über die Mixed Binary schreibe ich vielleicht später noch einen
anderen Artikel), daher sind alle Tasks unseres Programms PPC-Native.
Sollte man nun einen PPC-Native-Subtask starten, so muß dieser natürlich
mit CreateTaskPPC() der powerpc.library gestartet werden, nicht etwa mit
CreateTask(), weil das 68k-AmigaOS natürlich keine PPC-Tasks starten kann.
Der Aufruf entspricht jedoch bei praktisch allen "Exec-ähnlichen"
Funktionen der powerpc.library 1:1 dem Vorgehen unter dem 68k-AmigaOS. Eine
Alternative wäre natürlich die Verwendung eines Mixed Binary, aber das
sollte man vermeiden, da es die Performance senkt.
Ein weiterer Unterschied betrifft noch die Tag-List-Schreibweisen mancher
OS-Funktionen. So beherrscht der PPC-Compiler zwar OpenScreenTagList, aber
nicht OpenScreenTags. Man muß den Source also entsprechend anpassen.
Ein weiterer Unterschied betrifft die Funktion BeginIO der alib_protos.h.
Diese Funktion ist unter PPC nur über ein (im Beispiel für das
audio.device)
#include <libraries/powerpc.h>
#include <ppcamiga.h>
void BeginIOAudioPPC(struct IORequest *arg1)
{
extern struct Library *AudioBase;
ULONG regs[16];
regs[9] = (ULONG) arg1;
__CallLibrary(AudioBase,-30,regs);
}
verfügbar. Wie man sieht, muß man unter PPC grundsätzlich die LibBase des
Devices auslesen. Ein Beispielaufruf könnte wie folgt aussehen (Code-
Beispiel aus ZhaDoomPPC...):
AudioBase = (struct Library *)audio_io->ioa_Request.io_Device;
c = &channel_info[cnum];
c->audio_io->ioa_Request.io_Command = CMD_WRITE;
c->audio_io->ioa_Request.io_Flags = ADIOF_PERVOL;
c->audio_io->ioa_Data = &chip_cache_info[cache_chip_data
(id)].chip_data[8];
c->audio_io->ioa_Length = lengths[id] - 8;
c->audio_io->ioa_Period = period_table[pitch];
c->audio_io->ioa_Volume = vol << 2;
c->audio_io->ioa_Cycles = 1;
#ifdef PPC
BeginIOAudioPPC((struct IORequest *)c->audio_io);
#else
BeginIO ((struct IORequest *)c->audio_io);
#endif
Einige Leser fragen sich vielleicht inzwischen, wo der berühmte
Kontextswitch bleibt. Ganz einfach: Er bleibt. StormC verfügt nämlich über
das Feature des "vollautomatischen Kontextswitches", was bedeutet, der User
muß sich um nichts mehr kümmern, da dies bereits der Compiler für ihn
erledigt. Es müssen lediglich Stub-Dateien oder Defines für die per
Kontextswitch aufzurufenden Funktionen vorliegen. Diese sind für alle
AmigaOS-Funktionen sowie für die 68k-Funktionen der rtgmaster.library (die
jedoch auch PPC-Native-Funktionen besitzt) bereits in die ppcamiga.lib
integriert. Für Funktionen, die nicht integriert sind, generiert man die
Stubs mittels
genppcstub mylib_protos.h mylib.fd VERBOSE
wobei die Proto- und die FD-Datei vorliegen müssen. Das Ergebnis wird dann
einfach ins Projekt integriert (es handelt sich um einen C-Source). Im
Falle eines "Mixed Binary" geht übrigens der Wechsel zwischen 68k und PPC
wieder automatisch, ohne daß etwas Zusätzliches gemacht werden muß.
D) Kontextswitch-Optimierung
----------------------------
Unter WarpOS braucht ein Kontextswitch etwa 0.5 Millisekunden (hängt aber
auch von der verwendeten PPC-Karte ab, 0.5 Millisekunden braucht es bei
einer 200-MHz-Karte). Es sollte vermieden werde, "viele Kontextswitches pro
Sekunde" zu erzeugen.
Beispiele, was zu vermeiden ist:
- Byteweises Einladen von Files mit fgetc (statt dessen mit fread in einen
Fast-RAM-Buffer einladen, und dann nachbearbeiten)
- Pixelweises Darstellen auf dem Bildschirm (statt dessen in einen Fast-
RAM-Buffer zeichnen und den dann mit einem Befehl zeichnen)
- In häufig pro Sekunde aufgerufenen Schleifen viele OS-Calls
Die Grafik kann auch mittels der PPC-Version der rtgmaster.library komplett
PPC-Native abgearbeitet werden.
Ein Beispiel der Effekte von Kontextswitches:
ZhaDoomPPC auf einem 150-MHz-Rechner ohne Sound: 36 fps
ZhaDoomPPC auf einem 150-MHz-Rechner mit Sound : 32 fps
Der Sound bewirkt bei ZhaDoomPPC derzeit 9 Kontextswitches. Eine Lösung des
Problems wäre die Auslagerung der kompletten Sound-Funktion in einen 68k-
Part, den man dann über einen manuellen Aufruf des Kontextswitchers (siehe
Dokumentation der powerpc.library) oder über ein Mixed Binary aufruft.
Damit würde der Mehraufwand auf einen Kontextswitch reduziert. Im Falle von
ZhaDoomPPC war das leider nicht so einfach machbar, da die Kontextswitches
in verschiedenen Funktionen lagen, und so genau habe ich mir das noch nicht
angeschaut.
Es sind zudem noch Optimierungen durch Ausnutzung der BAT-Register des PPC
möglich, hier möchte ich jedoch einfach auf die Dokumentation von WarpOS
verweisen.
E) Weitere Anpassung
--------------------
Es ist natürlich möglich, Teile des Codes in PPC-Assembler zu übersetzen,
aber im Normalfall ist das eigentlich gar nicht nötig. Der PPC ist
eigentlich schnell genug, um (fast) alles in C oder C++ zu machen
(rtgmaster PPC hat trotzdem einige Assembler-Parts, da dies bei einem
Grafiktreiber Sinn macht).
In einigen Newsgruppen wurde diskutiert, Programmteile parallel auf dem 68k
und dem PPC laufen zu lassen. Dies würde man mittels des Message-Systems
von WarpOS implementieren (Stichwort AllocXMsg in der powerpc.library-
Dokumentation), man sollte es jedoch aus zwei Gründen vermeiden:
1) Ein solches Programm würde bei (zukünftigen) PPC-only Maschinen nicht in
idealster Weise profitieren, da Teile immer noch als 68k-Code abgearbeitet
würden. Und ich denke, auf lange Sicht werden solche Maschinen kommen.
2) Die Hardware ist schlichtweg nicht für den Multiprozessor-Betrieb
geeignet, ganz gleich, wie man die Software anstellt. Ich werde hierauf
nicht näher eingehen, ist in den Newsgruppen genug zu Tode diskutiert
worden.
Solange die Tasks nicht miteinander kommunizieren, dürfte es
unproblematisch sein, aber dann machen sie auch vermutlich nicht viel Sinn.
Dies ist ein prinzipielles Problem der Hardware.
Aus oben genannten Gründen kann ich nur empfehlen, "synchron" zu bleiben.
Wobei zukünftige Kompatibilität bei optimaler Geschwindigkeit da wohl
beinahe das Wichtigere ist.
Kontaktadresse bei Rückfragen:
------------------------------
Steffen Häuser
Limburgstr. 127
73265 Dettingen/Teck
Tel. 07021-51787
email: MagicSN@Birdland.es.bawue.de
Informationen zur Person:
Informatik-Student.
Haupt-Aktivitäten im Amiga-Bereich:
- rtgmaster.library
- ZhaDoomPPC (Ein PPC-Doom-Port unter WarpOS)
- WarpView (Ein Image-Viewer für PPC unter WarpOS und 68k)
- WarpAMP (ein Port des AMP-MPEG-3-Players für WarpOS)
- Crystal3D (ein Port einer Spiele-artigen 3D-Engine)
- Seit kurzem Coder bei der Demo-Gruppe "Giants"
- ehemals Autor der "Amiga Texturemapping FAQ"
- "Berater" in Grafikkarten/PPC-Fragen bei einigen Amiga-Spiele-Firmen
- ppctut.guide (Einführung in PPC-Assembler-Programmierung)«