----> CODERS.GER-MAG 1/98 <---- [============================================================================] Editorial [============================================================================] Lange, sehr lange ist es her das das letzte Coders.ger Mag bei seinen Lesern ankam. Leider bin ich momentan sehr beschaeftig ich schreibe fast jede Woche eine Klausur. Deshalb kann es sein das die Naechsten ausgaben vom Coders.ger Mag in etwas unregelmaessigen Abstaenden fertig gestellt werden. :( Zum Inhalt will ich diesmal nicht viel sagen. Schaut es euch einfach an. Wir suchen dringend jemand der fuer einen geplanten Event-Corner in den NEWS anstehende Parties und Daten dazu zusammenstellt. Wenn du Interesse hast melde dich bei einer unserer Emails. bis dann BJ [BJ/Mag Team] -----------------------------------------------------------------------------/ [============================================================================] Inhalt [============================================================================] LESERBRIEFE : WERBUNG : NEWS : #CODERS.GER : Wie man das Channelmag bekommen kann Legal Stuff oder Wie ich einen Artikel einsende HARDWARE CORNER : FUN CORNER : NET CORNER : Interessante WWW Seiten TIPS&TRICKS : INTERVIEW : KURSE : C KURS fuer Einsteiger (Teil 4) Hugi Assembler KURS (Teil 3) -----------------------------------------------------------------------------/ [============================================================================] +++ News +++ News +++ News +++ News +++ News +++ [============================================================================] --[Microsoft jetzt so richtig in der Klemme]---------------------------------- Nach den Untersuchungen in der Microsoft/Kartell-Affäre der letzten Tage ist es nach Aussagen von Experten möglich, den Internet Explorer innerhalb von 90 Sekunden zu deinstallieren. Für US-Richter Thomas Jackson Grund genug, zunächst gegen den Antrag Microsofts zu entscheiden: Die Redmonder hatten in ihrem Antrag argumentiert, man könne die aktuelle Windows-Version nicht vom Explorer trennen und müsse deshalb eine veraltete Windows-Version ausliefern. Andernfalls laufe das Betriebssystem nicht mehr stabil. Die nächste Anhörung ist für den 13. Januar 1998 festgelegt. Die Partei des Klägers, wie auch die des Angeklagten, darf jeweils nur einen Experten vorsprechen lassen. Die Bitte Microsofts um Beschleunigung des Verfahrens wurde von Richter Jackson abgelehnt. Nur kurz auf der Microsoft-Seite http://www.microsoft.com -----------------------------------------------------------------------------/ --[Internet Explorer wird auf Netscape-Web-Site gelöscht]--------------------- Netscape wartet das endgültige Urteil in Microsoft-Kartell-Affäre gar nicht erst ab: Wie sonst läßt sich erklären, daß bereits eine Deinstallations- Software für den Microsoft Internet Explorer bereitsteht. Auf der Homepage von Netscape, bietet ein Button die Deinstallation des Browsers, in Verbindung mit der Installation des eigenen Produktes an. Laut Aussagen des Pressesprechers von Netscape, muß dem Kunden die freie Wahl seines Browsers selbst überlassen bleiben. Explorer löschen? http://www.netscape.com -----------------------------------------------------------------------------/ --[HTML 4.0 im Kommen]-------------------------------------------------------- Nach mehreren Monaten segnete das W3- Konsortium den neuen Standard HTML 4.0 endlich ab. HTML 4.0 beinhaltet unter anderem Inline-Frames, verbesserte Tabellenfunktionen und unterstützt Audio- und Videoelemente auf der Web- Site. Auch die Geschwindigkeit von bisher eher trägen Video-Streams soll um einiges schneller werden. Der Surfer muß, um alle Annehmlichkeiten nutzen zu können, auf die neuen Browser-Genrationen von Netscape, Microsoft und konsorten warten. Wers nicht mehr aushält, kann die offiziellen Spezifikationen des Konsortiums von deren Web-Site abrufen. HTML 4.0 in den Startlöchern http://www.w3.org -----------------------------------------------------------------------------/ [============================================================================] #Coders.ger [============================================================================] Wie man das Channelmag bekommen kann Wie die meisten von euch wissen hat der Channel #Coders.ger ein Homepage im WWW. (http://home.pages.de/~coders.ger) Seid ihr Vieluser dieses Channels koennt ihr euch dort in den Mailveteiler eintragen und bekommt neben dem Mag noch andere Anfragen und Informationen von Channelusern. Wenn ihr dieses aus irgendeinem Grund nicht tun wollt gibt es noch die Moeglichkeit das Mag mit Hilfe einer Mail zu abonnieren. Schick eine Mail an : The_Coders.ger_Mag@usa.net Ins Topic : "ich möchte bitte gerne das Mag beziehen, wenn es nicht zuviel Umstände macht und ich würdig genug bin dieses göttliche Teil zu erhalten" ;) Oder einfach "Mag her!" und in den Body die Emailaddresse an die ihr es geschickt haben wollt. Legal Stuff oder Wie ich Artikel einsende Mit Erhalt dieses Mag habe ich mich verpflichtet fuer jede jemals erscheinende Ausgabe des Coders.ger Channelmags mindestens einen Beitrag zu leisten, d.h. einen Artikel zu schreiben. Diese Verpflichtung gilt ueber den Tod hinaus. Artikel an : The_Coders.ger_Mag@usa.net P.S.: Die Artikel sollten in der Form in der ihr sie uns Schickt frei kopierbar sein. -----------------------------------------------------------------------------/ [============================================================================] Net Corner [============================================================================] --[Einige Links zum Thema: Free Emails]--------------------------------------- o http://www.gmx.net Freie eMail inkl. Mailinglist o http://www.uni.de Schoene eMail "yourname@uni.de" -----------------------------------------------------------------------------/ --[Einige Links zum Thema: html Scripts]-------------------------------------- -cgi o http://www.cgi-free.com/ CGI-Free (einfach geil) -Java o empty ... -----------------------------------------------------------------------------/ --[Einige Links zum Thema: Free Counters]------------------------------------- o http://www.compushop.de/join.htm Compushop.DE (der laeuft immer) o http://www.count4all.com/ Count for all -----------------------------------------------------------------------------/ --[Einige Links zum Thema: Search Engines]------------------------------------ o http://www.looksmart.com/r?l3p&h1 LookSmart -beste search engine die ich kenne. Sucht nach emails oder nach euren namen. der findet alles ... o http://www.mircx.com/ MircX - search engine auch fuer MP3 -----------------------------------------------------------------------------/ --[Einige Links zum Thema: Game & Fun]---------------------------------------- -C64 Games fuer euren C64 Simulator (d64 und t64 files) o http://www.bath.ac.uk/~ma4rms/c64/games.html C64 Games for the Emulators o http://www.xs4all.nl/~dazzler/c64.html CBM Nostalgia o http://debra.rau.ac.za/C64/ The Commodore 64 Web (alles vorhanden) -----------------------------------------------------------------------------/ --[Einige Links zum Thema: Music]--------------------------------------------- -music compos o http://www.3awebcon.com/hsd/fosh/ FOSH Music Competition o http://www.musicwerx.com/ HARDCoR: The MusicwerX Compo II o http://hem1.passagen.se/pmx/ Music pool X-mas music compo'97! o http://members.tripod.com/~module/ The Complete Module Compo o http://hem1.passagen.se/mists/c/index2.html Trash Compo -gemischtes aus der music scene o http://www.united-trackers.org/ United Trackers (einfach VEREINT) o http://globec.globec.com.au/~hunz/ Hunz/FM (der neue fuenfte der FM's) o http://www.ipoline.com/~bswmui/ IceZone v2.0 (tolle page) o http://internet.sk/modparada/html/scenar02.html (hier ist auch PM/FC zu sehen) -music player o http://pcuf.fi/~markoh/mld/ Home of The Unchained Melody o http://www.castlex.com/modplug/ Kim's MOD Page | MODPlug Central o http://www.stack.nl/~mikmak/mikit.htm MikIT o http://www.mod4win.com/ Mod4Win Site o http://www.uni-koblenz.de/~kilroy/ Releases of the famous XTC player o http://www.ThePentagon.com/cubic The OFFICIAL Cubic Player Homepage o http://homes.rhein-zeitung.de/~amautsch/xtc-play.htm xtc-play -music samples/instruments o http://www.futurenet.com/samplenet/ Samplenet -invitations (your music- utils/tools/players on a cd-rom) o http://www-hoaxarts.org/tm97/ Tracker Meeting '97 -----------------------------------------------------------------------------/ [============================================================================] Kurse [============================================================================] --[C - Kurs Teil 4]----------------------------------------------------------- Alles Array oder was ? Fuer alle die nicht wissen was ein Array ist erstmal ne Definition: (Wird wohl keiner sein aber trotzdem, besser ist besser) Ein Array ist keine Variable, sondern ein typ, welcher einen Zusammenschluss/Feld (eben ein array) von Variablen, die eines beliebigen typs sein koennen, definiert. Bespiel 5.1 /* Ein Array a vom typ char mit 5 Elementen */ char a [5]; Das Ganze sieht nun im Speicher so aus : a[0] a[1] a[2] a[3] a[4] char char char char char Achtung !!! In C wird bei der Definition eines Array die Anzahl der Elemente angegeben, bei der Abfrage jedoch dem ersten Element die 0 zugewiesen. Wird in einem Programm in dem a so defieniert ist wie in Beispiel 5.1 mit a[5]=100 irgend ein Wert zugeordnet kann das zum Absturtz oder sonstigem wirren Verhalten fuehren. Arbeiten mit Arrays Zuweisen und Auslesen des Inhalts von Arrayvariablen geht analog zu normalen Variablen Beispiel 5.2 void main () { int zahlen [5] ; /* Array der laenge 5 vom Typ int */ Zahlen [0]=100 ; /* Dem ersten Element wird 100 zugewiesen */ Zahlen [1]=Zahlen [0] *2 ; /* Dem zweiten Element wird 2 mal Inhalt des Ersten zugewiesen (also 200) */ ... } Intialisierung von Arrays bei der Definition In vielen Faellen moechte man das ein array schon bei Start eines Programms bestimmte werte enthaellt. Man kann sich dann eine komplizierte Schleife schreiben die diese Werte beim Start setzt. Einfacher geht das wenn man diese werte schon bei der Definition setzt. Beispiel 5.3 /* Definiert ein array zahlen mit der laenge 5 und weisst */ /* Zahlen [0]=1 ; Zahlen [1]=2 ;Zahlen [3]=3 ; usw. zu. */ int zahlen[5] = { 1,2,3,4,5 }; Allgmein : Typ Name[n] = { erster-wert, zweiter-wert,...,n-ter-wert } In Beispiel 5.3 kann man statt " int zahlen[5] = {1,2,3,4,5};" auch " int zahlen[] = {1,2,3,4,5};" schreiben. Der Compiler erkennt dann an der Anzahl der Initialisierten Elemente die laenge des Arrays Man kann auch nur einen Teil eines Arrays Initialisieren : Beispiel 5.4 int zahlen [5] = {1,2,3} Diese Zeile Bewirkt das die Elemente 0 bis 2 mit den werden 1 2 3 initialisiert werden, waehren alle folgenden Elemente uninitialisiert bleiben. Beispiel 5.5 /* Array Palette (768 chars) auf 0 initialisieren */ char Palette[768] = {0} ; /* so und ab hier kann dann ne fadein schleife stehen :) */ So dass wars schon von den Arrays, nein noch nicht ganz : Wir haben bisher nur eindimensionale Arrays behandelt. Es gibt aber auch noch mehrdimensionale Arrays, diese behandelt man analog zu den eindimensionalen nur das man statt einer '[]' mehrere hat. Beispiel 5.6 /* Definition eines 2 Dimensionalen Feldes (Arrays) */ int spielfeld [3][4] = { 0 , 1 , 2 , 3 4 , 5 , 6 , 7 8 , 9 , 10 , 11 } ; Im Beispiel wird Spielfeld[0][0] mit 0 initialisiert, Spielfeld[0][1] mit 1 , ... , Spielfeld [3][4] mit 11. -----------------------------------------------------------------------------/ --[Hugi s Assembler Kurs Teil 3]---------------------------------------------- Hugi Assembler Kurs Teil 3 (Folge 4) Dieser Artikel wurde uns freundlicherweise von Adok/Hugi Diskmag zur verfuegung gestellt. Der Kurs "Adok's Way to Assembler" ist Freeware. Das heisst, dass er in unveraenderter Form frei kopiert und weitergegeben werden kann. Das Copyright liegt bei Adok (Claus-Dieter Volko). (nochmals herzlichen Dank Adok) Adok's Way to Assembler Folge 4 +++ Flag-Register +++ Das Flag-Register, die Steuerzentrale des Computers, ist das einzige Prozessorregister, das nicht über den MOV-Befehl angesprochen werden kann. Wie man es anspricht, werden wir noch später erfahren. Das Flag-Register ist 16 Bit breit, wobei jedes einzelne Bit eine eigene Funktion erfüllt. In dieser Kursfolge werden wir folgende zwei Flags benötigen: - Bit 0 - Carryflag: Dieses Flag wird u.a. dann automatisch gesetzt, wenn bei einer mathematischen Operation ein Über- oder ein Unterlauf entsteht. -> Das Carryflag wird gesetzt, wenn die Quelle eines CMP-Befehls - wie wir ihn bald kennenlernen werden - größer als das Ziel ist. - Bit 6 - Zeroflag: Ist das Ergebnis eines Befehls gleich 0, so wird dieses Bit automatisch gesetzt. -> Dieses Flag ist wichtig, um die Funktionsweise des JE-Befehls, welchen wir ebenfalls bald kennenlernen werden, zu verstehen! Weitere Flags, die wir nicht unmittelbar brauchen werden, aber ganz nützlich sind: - Bit 8 - Trapflag: Ist dieses Flag gesetzt, wird nach jedem ausgeführten Befehl INT 1 ausgelöst. (Das machen sich auch Debugger zunutze, um nach jedem Befehl das Programm zu unter- brechen und dem Benutzer/Hacker den aktuellen Status der Register und des Programms anzuzeigen.) - Bit 9 - Interrupt-Enable-Flag: Dieses Flag läßt sich mit dem Befehl STI setzen und mit CLI löschen. Ist es nicht gesetzt (also gleich 0), sind alle Interrupts außer den sogenannten NMIs (Non-Maskable-Interrupts, zu deutsch: nichtmaskier- bare Unterbrechungen) deaktiviert. - Bit 10 - Directionflag: Bei sogenannten String-Befehlen wird angezeigt, ob ein Block in aufsteigender (Flag=0) oder absteigender (Flag=1) Reihenfolge abgearbeitet werden soll, also das Register, das auf das aktuelle Element zeigt, jedesmal erhöht oder erniedrigt werden soll. Zu diesem Zweck läßt sich das Directionflag auch vom Programmierer selbst direkt setzen & löschen, und zwar mit den Befehlen STD und CLD. Der Vollständigkeit halber erwähne ich auch die weniger interessanten Flags: - Bit 2 - Parityflag: Hat das Ergebnis einer Operation eine gerade Anzahl von gesetzten Bits, so ist dieses Flag gesetzt. Wird von vielen DFÜ-Programmen bei der CRC-Prüfung der seriellen Schnittstelle verwendet. - Bit 4 - Auxiliary-Carryflag: Entspricht dem Carryflag, wird aber nur dann gesetzt, wenn man mit sogenannten BCD-(Binary-Coded- Decimals)-Zahlen arbeitet. BCD-Zahlen werden allerdings kaum verwendet, weil sie ziemlich speicherintensiv sind. - Bit 7 - Signflag: Entspricht dem höchstwertigen Bit des Ergebnisses der letzten Operation. (Das Signflag wird vor allem bei vor- zeichenbehafteten Zahlen benötigt. Dort gibt nämlich das höchste Bit das Vorzeichen an (1=minus, 2=plus). Dies ist auch der Grund, warum in allen Programmiersprachen signed-Datentypen einen kleineren Höchstwert haben als unsigned-Datentypen. In diesem Kurs werden wir aber wahrscheinlich nicht näher auf vorzeichenbehaftete Zahlen eingehen.) - Bit 8 - Overflowflag: Ist ebenfalls nur dann interessant, wenn mit vorzeichenbehafteten Zahlen gearbeitet wird. (Rechnet man bspw. 60 + 70, so ergibt dies 130. Damit wird aber das höchste Bit des Ergebnis-Bytes gesetzt, das als Vorzeichen betrachtet zu einem falschen Ergebnis führen würde. Deshalb wird in solchen Fällen automatisch das Overflowflag gesetzt.) +++ Vergleiche in Assembler +++ Wozu nun das Ganze? Nun, in ASM gibt es keine IF/THEN-Konstrukte wie in den Hochsprachen. Stattdessen existiert ein ganz besonderer Befehl, CMP (nicht zu verwechseln mit CP/M :-)) ). Syntax: CMP Ziel,Quelle Ziel und Quelle sind die zu vergleichenden Werte, also Zahlen, Register oder Speicherstellen. Wie arbeitet CMP? Ganz einfach: CMP zieht die Quelle vom Ziel ab, wobei im Gegensatz zum "richtigen" Subtraktionsbefehl, SUB, die Regs bzw. Speicherstellen nicht verändert werden. Das Geniale an der Sache ist nun: Je nachdem, welches Ergebnis bei der Subtraktion herauskommt, werden bestimmte Flags gesetzt oder gelöscht! - Sind Ziel und Quelle identisch, ist das Ergebnis gleich 0 - also wird das Zeroflag gesetzt. Andernfalls wird das Zeroflag gelöscht. - Ist die Quelle größer als das Ziel, entsteht ein Unterlauf - also wird das Carryflag gesetzt. Andernfalls wird das Carryflag gelöscht. Und jetzt kommt's: Es gibt verschiedene bedingte Sprungbefehle, die auf bestimmte Register reagieren. - JE und JZ: Der Sprung zum angegebenen Label wird nur dann durchgeführt, wenn das Zeroflag gesetzt ist. -> Dieser Sprungbefehl wird verwendet, wenn man überprüfen will, ob zwei Werte identisch sind. - JNE und JNZ: ...wenn das Zeroflag nicht gesetzt ist. -> ...ob zwei Werte ungleich sind. - JA: ...wenn weder das Carry- noch das Zeroflag gesetzt ist. -> ...ob das Ziel größer als die Quelle ist. - JB: ...wenn das Carryflag gesetzt ist. -> ...ob die Quelle größer als das Ziel ist. Hängt man an JA bzw. JB noch ein E dran (JAE, JBE), so wird aus "größer" "größer oder gleich". Nun habe ich noch zwei HOT TIPS für euch! :-) - Wenn man prüfen will, ob das CX-Register gleich 0 ist, kann man sich CMP ersparen! Der Befehl JCXZ führt einen Sprung aus, wenn CX=0. - Will man prüfen, ob ein Wert gleich 0 ist, so müßte man nach dem, was wir gelernt haben, schreiben: CMP Wert,0 JE Label Es geht aber auch so: OR Wert,Wert JE Label Hiermit wird der angegebene Wert mit sich selbst OR-verknüpft. Dadurch wird der Wert nicht geändert, aber, wenn der Wert 0 ist, das Zeroflag gesetzt. Statt OR kann man auch AND oder TEST schreiben. Alle drei Möglichkeiten sind um einige Taktzyklen schneller als CMP. Folgendes Beispielprogramm demonstriert die Verwendung des CMP-Befehls. MODEL TINY ;Für COM-Files CODE SEGMENT ;Beginn Code-Seg ASSUME CS:CODE,DS:CODE ;CS und DS zeigen auf Code-Seg ORG 100h ;Startadresse COM start: ;Startlabel JMP begin ;Sprung zu Label begin wert1 DB 10 ;Variable wert1 wert2 DB 0 ;Variable wert2 text1 DB "Werte gleich$";Meldung 1 text2 DB "Wert1 größer$";Meldung 2 text3 DB "Wert2 größer$";Meldung 3 begin: ;Beginn des Proggys MOV BH,BYTE PTR wert2 ;Wert 2 auf BH CMP wert1,BH ;Werte vergleichen JNE ungleich ;Wenn ungleich -> Label ungleich MOV DX,OFFSET text1 ;Ansonsten Meldung 1 JMP ausgabe ;Sprung zu Label ausgabe ungleich: ;Wenn ungleich... JB w2groesser ;Wenn W2 größer -> Label w2groesser MOV DX,OFFSET text2 ;Ansonsten Meldung 2 JMP ausgabe ;Sprung zu Label ausgabe w2groesser: ;Wenn Wert 2 größer... MOV DX,OFFSET text3 ;Meldung 3 ausgabe: ;Meldung ausgeben MOV AX,0900h ;Funkt. 9 INT 21h ;String ausgeben MOV AX,4C00h ;Funkt. 4Ch INT 21h ;Programm beenden CODE ENDS ;Ende Code-Seg END start ;Ende des Proggys Um deutlich zu machen, daß CMP sowohl mit Registern als auch mit Speicher- stellen arbeiten kann, ist in diesem Programm der eine Parameter ein Register, der andere eine Speicherstelle. Das Programm vergleicht nun die beiden Werte und gibt aus, ob sie gleich sind oder welcher der beiden größer ist. Setzt einfach in die Variablendefinitionen andere Zahlen ein und compiliert das Programm neu, um zu sehen, welche Auswirkungen dies hat. Spielt euch auch herum, probiert, andere Sprungbefehle zu verwenden - solange, bis ihr die Funktionsweise eines jeden Sprungbefehls versteht. +++ Stack +++ Der Stack (Stapel, "Kellerspeicher") ist eine besondere Art von Datensegment, das mit den Befehlen PUSH und POP angesprochen wird. Das Register SS zeigt immer auf das Segment des Stacks und das Register SP (Stackpointer) auf die aktuelle Position im Stapel. Nehmen wir an, SP zeige auf den Offset 1000. Nun schreiben wir PUSH AX. Damit wird der Inhalt von AX auf den Offset 1000 im Stacksegment geschrieben. Gleichzeitig wird dabei der Inhalt von SP um zwei - denn AX ist ein Word, also 2 Bytes groß - erniedrigt. Jetzt schreiben wir PUSH BX. Nun wird der Inhalt von BX auf den Offset 998 im Stacksegment geschrieben und der Stackpointer um zwei weitere Bytes erniedrigt. Er zeigt nun also auf den Offset 996. Mit dem Befehl POP lassen sich Werte vom Stapel zurückholen. Dabei muß berück- sichtigt werden: Das Ganze arbeitet nach dem sogenannten LIFO-Prinzip - Last In, First Out. Das bedeutet: Der zuletzt gePUSHte Wert wird als erster gePOPt. Wenn man den Stack mit einem schmalen Keller vergleicht, wird man erkennen, daß es ja ganz logisch ist: Nehmen wir an, ich werfe einen Fernseher in den Keller, danach eine Waschmaschine. Wenn ich wieder einen Gegenstand zu mir nehmen will, muß ich zuerst die Waschmaschine - das als letztes hinein- geworfene Objekt - nehmen, dann den Fernseher (abgesehen davon, daß der Fern- seher sowieso schon beschädigt sein wird :-) ). Genauso verhält es sich mit den Werten am Stack. Schreibt man nun z.B. POP AX, so wird der letzte Wert vom Stack geholt, in AX geschrieben und der Stackpointer um zwei erhöht. Dabei muß betont werden, daß der Wert nicht vom Stack gelöscht wird! Er bleibt noch auf der Speicherstelle, auf die er gePUSHt wurde - lediglich zeigt jetzt der Stackpointer auf ein anderes Element. Und wenn wir ein Word POPen, z.B. POP CX, wird SP wiederum um zwei erhöht. Somit zeigt er in unserem Beispiel wieder auf den Offset 1000. Drei wichtige Dinge, die beim Arbeiten mit dem Stack berücksichtigt werden müssen: - PUSH und POP funktionieren nur 16-bittig! PUSH BL bspw. würde also nicht funktionieren. - Die Anzahl der PUSHs und der POPs müssen einander ausgleichen, so daß der Stackpointer am Schluß wieder auf den Offset zeigt, auf den er vorher gezeigt hat. - Verwendet man den Stack in COM-Dateien, so zeigt das SS-Register natürlich auf das Codesegment und der Stackpointer auf das letzte Byte im Codesegment, nämlich CS:FFFFh. Normalerweise ist dies unproblematisch. Problematisch wird es nur dann, wenn das Programm so groß ist, daß beim PUSHen die letzten Befehle überschrieben werden. Also Vorsicht! So, das war's für heute! In der nächsten Folge geht's weiter, und natürlich wünsche ich euch auch diesmal bis dahin viel Spaß! Adok! -----------------------------------------------------------------------------/ [============================================================================] t h e e n d [============================================================================] Redaktion ...................... Bj, ASP Newsmanager .................... Melcom writers ........................ Dynamite Melcom Bj Adok others (more writers wanted) design ......................... Melcom Copyright (c) 1997 CODERS.GER TEAM - the_coders.ger_mag@usa.net