ST-Ecke: Joystickabfrage & Farbregister

"Willkommen bei der zweiten Folge unserer ST-Ecke! Ich hoffe, daß es sich unter unseren Lesern herumgesprochen hat, daß wir eine „Tips & Tricks-Kolumne“ eingerichtet haben. Bei uns treffen inzwischen die ersten interessanten Fragen ein, denen wir uns in den kommenden Ausgaben widmen werden. Zunächst wollen wir uns mit einem Thema beschäftigen, nach dem wir schon häufig gefragt wurden. Ich rede von der Joystickabfrage. Offensichtlich ist sie nicht so einfach wie auf Homecomputern, wie dem ATARI XL oder dem C-64. Aber keine Angst: Zauberei ist es auch nicht.

Zuerst muß man bedenken, daß es keine bestimmte Speicheradresse gibt, in der man diesen Joystickwert findet. Da auch GEM sich irgendwann einmal ändern könnte, hat man diese Adresse vom Betriebssystem abhängig gemacht. Daher läuft der ganze Vorgang etwas aufwendiger ab. Sie werden weiter unten eine Routine finden, die diese Joystickabfrage durchführt und die gut dokumentiert ist. Sie ist in (MEGAMAX-) C geschrieben und benutzt den INLINE-ASSEMBLER. Für diejenigen, die sich in Assembler auskennen, dürfte es kein Problem sein, die Routine vollständig in Assembler zu implementieren. Für alle Basic-Fans sei hier gesagt, daß wir eventuell eine Basic-Implementierung nachliefern werden. Sollten Sie übrigens einige der hier veröffentlichten Routinen in andere Sprachen (PASCAL, MODULA, BASIC etc.) übersetzt haben, würden wir diese Routine gerne in unseren Public-Domain-Service aufnehmen. Wir stellen sie dann auf einer gesonderten Disk zusammen. So wollen wir unsere Tips & Tricks allen Lesern zugänglich machen. Sie werden verstehen, daß wir bei der Fülle von Programmiersprachen die Ideen nicht immer in alle Sprachen übersetzen können.

Nun aber zu der Joystickabfrage:

Im Atari ST befindet sich eine Tabelle, in der unter anderem auch die Adresse der aktuellen (eventuell von GEM) genutzten Joystickroutine zu finden ist.

Diese Adresse der Tabelle erhalten wir, indem wir die Xbios-Funktion Nr. 43, Kbdvbase() genannt, aufrufen. Die Tabelle ist folgendermaßen organisiert:

midivec
Adresse der MIDI-Eingabe-Routine

vkbderr
Adresse der Tastatur-Fehler-Routine

vmierr
Adresse der MIDI-Fehler-Routine

statvec
Adresse der IKBD-Status-Routine

mousevec
Adresse der Maus-Routine

clockvec
Adresse der Uhr-Routine

joyvec
Adresse der Joystickroutine

Da jede Adresse vier Bytes lang ist, addieren wir nun 28 zum Tabellenanfang so können wir aus dieser Adresse die Anfangsadresse der Joystickroutine herauslesen. Diese Adresse speichern wir zunächst ab, um sie später, wenn wir nicht mehr auf den Joystick zugreifen wollen, wieder zurückzuschreiben. Dann setzen wir die Adresse unseres Programmteils ein.

Wie muß nun eine Joystickroutine geschrieben sein?

Eine Joystickroutine ist kein Programmteil, der gleich irgendwelche Aktionen auf eine bestimmte Bewegungsrichtung des Joysticks ausführt. Vielmehr sorgt diese Routine dafür, daß die vom Joystick kommenden Werte an eine bestimmte für das Programm leichter erreichbare Stelle geschrieben werden. Die Routine wird beim Bewegen des Joysticks angesprungen. Dabei steht im Prozessorregister A0 die Adresse, an der sich die Werte der beiden Joysticks finden. Diese Werte übernehmen wir nun (siehe Listing) und schreiben sie dann in die Adresse einer für das C-Programm globalen Variablen. Dadurch kann man aus dieser Adresse beziehungsweise aus der Variablen die Joystickwerte auslesen. Prinzipiell ist dies der ganze Trick. Als Erweiterung wäre denkbar, daß man ein Programm schreibt, das eine (residente) Routine initialisiert, die aus der Joystickroutine ein eigens definiertes Ereignis (EVENT) in die Message-Pipeline von GEM schreibt. Natürlich könnte diese Routine auch direkt irgendwelche Objekte auf dem Bildschirm bewegen. Der Phantasie sind keine Grenzen gesetzt. Um die Joystickroutine auch zu initialisieren, muß dem Tastaturprozessor ein Code geschickt werden, der ihn so programmiert, daß er ab sofort die Joystickwerte entgegennimmt. Dieser Wert ist die Zahl Hexadezimal 14. Mit den Werten 15H und 8H wird die Mausabfrage wieder eingestellt. Anhand der kurzen Erklärung und der ausführlichen Dokumentation des Liftings müßte eine eigene Implementierung recht einfach zu bewerkstelligen sein.

Die Codierung des Joystickwertes ist ganz einfach:

Bit Bedeutung bei gesetztem Bit
0 Joystick nach oben gedrückt
1 Joystick nach unten gedrückt
2 Joystick nach links gedrückt
3 Joystick nach rechts gedrückt
4-6 nicht benutzt
7 Feuertaste gedrückt
#include <osbind.h>

long oldjoy;                /* enthält später die Adresse der alten */
                            /* Joystickroutine */ 
char code=0x14;             /* Code zum Einschalten der Joystickpakete */
char mausan[]={0x15,0x8};   /* Code zum Wiedereinschalten der Maus */

char sticks[2];             /* Enthält die Joystickwerte */
long *adr;

main()
{
    joy(sticks);            /* Initialisierung der Joystickroutine */
    while (!Cconis())       /* Solange keine Taste gedrückt wird */
    {
        printf("%2x %2x\n",sticks[0],sticks[1]);
    }
    Ikbdws(1,mausan);       /* Maus einschalten */
    adr = (long*)(Kbdvbase()+24); /* Adresse des Joystickvektors*/
    *adr= oldjoy;           /* Alte Adresse zurückschreiben */
}

joy(stick)
char stick[]; /* Übergabevariable */
{
    asm{
    lea werte,A0        /* Adresse des Platzes, in den die Werte */
                        /* geschrieben werden. */ 
    move.l 8(A6),(A0)   /* Adresse der Übergabevariablen nach A0 */
    move #34,-(A7)      /* Kbdvbase: Adresse der alten Joystick */
                        /* routine holen und dann umbiegen' */ 
    trap #14            /* Springe zum xbios */
    addq.l #2,A7        /* Stack in Ordnung bringen */
    move.l D0,A0        /* In D0 steht der Tabellenanfang */
    move.l 24(A0),oldjoy(A4)/* Tabelleanfang +24 =Joystickroutinenadresse */ 
    lea newjoy,A1       /* Adresse unserer Routine hineinschreiben */
    move.l A1,24(A0)
    pea code(A4)        /* Code, damit der Tastaturprozessor ab sofort */
                        /* Joystickpakete schickt */ 
    clr -(A7)           /* Lösche Stack */
    move #25,-(A7)      /* xbios(25)= Ikbdws -> Code an den Tastatur- */
    trap#14             /* Prozessor schicken */
    addq.l #8,A7        /* Stack in Ordnung bringen */
    jmp ende            /* Ans Ende der Routine springen */

newjoy:                 /* Unsere neue Joystickroutine */
    movem.l A0-A1,-(A7) /* Register vorsichtshalber retten... */
    move.l werte,A1     /* Adresse für Platz nach A1 */
    addq.l #1,A0        /* In A0 steht Adresse der Pakete */
                        /* Adresse A0: Header */
                        /* A0+1: Joystick 1 Wert */
                        /* A0+2: Joystick 2 Wert */ 
    move.b (A0)+,(A1)+  /* Schreibe 1. Wert in dafür vorgesehen Platz */
    move.b (A0),(A1)    /* Schreibe 2. Wert in zweiten Platz */
    movem.l (A7)+,A0-A1 /* ...und schreibe die Register zurück */
    rts                 /* Das war's */
    werte:              /* Reservierter Platz für Adresse der Werte- */
    dc.l 0              /* Variablen */
    ende:
    }
} 

Listing 1:

Als nächstes Thema möchte ich ein kleines Anliegen Vorbringen. In letzter Zeit wurde es bei einigen Programmierern zur Unsitte, den Inhalt der Farbregister in Programmen zu ändern und beim Verlassen der Programme nicht wieder zu restaurieren - mit dem Erfolg, daß das Arbeiten nach dem Verlassen dieser Programme im DESKTOP wegen der verstellten Farben kaum mehr möglich ist. Deshalb möchte ich zeigen, wie man die Farben setzt und wie man sie vorher rettet:

Listing 2:

#inc1ude <osbind.h> /* Wir benutzen XBIOS-Routinen */

int palette[16]; /* Feld in dem die Farben gespeichert werden */
main()
{
    puts("Rette Farben");
    rette_farben();             /* Farben in palette[] abspeichern */
    Cnecin();                   /* Auf Tastendruck warten - ohne Echo */
    puts("Generiere neue Farben");
    zufallsfarben();            /* Irgendwelche Zufallsfarben setzen */
    Cnecin();                   /* siehe oben*/
    puts("Restauriere alte Farben");
    hole_farben();              /* Originalfarbe zurückholen */
    Cnecin();                   /* siehe oben */
}

rette_farben()
{
    register int farbindex; /* Farbennummer */

    /*Durch Übergabe der -1.wird die aktuelle Farben zurückgegeben.*/

    for (farbindex=0; farbindex<16; farbindex++)
        palette[farbindex]=Setcolor(farbindex,-1);

}

hole_farben()
{
    register int farbindex; /* Farbennummer */
            /* Schreibe die Farben wieder zurück */

        for (farbindex=0; farbindex<16; farbindex++)
            Setco1or(farbindex,pa1ette[farbindex]);

}

zufallsfarben()
{
    register int farbindex,farbe;

    for (farbindex=0; farbindex<16; farbindex++)
    {
        farbe=Random()&0x777;
        Setcolor(farbindex,farbe);
    }
}

Das „Retten“ der Farben erreicht man, indem man der Funktion Setcolor als Farbe eine -1 übergibt. Dann übergibt sie die aktuelle Farbe des Farbregi-sters. Speichert man die Farbregister vor dem Ändern ab, ist es leicht, diese Werte wieder zurückzuschreiben. Das Zurückschreiben könnte man auch durch folgenden Befehl erreichen, der sogar die Schleife in hole_farben() erspart:

Setpalete(palette);
/* Restaurieren der Farben */

Man sieht, daß das Retten und Restaurieren der Farben wirklich keine Zauberei ist!

Zur Abrundung wollen wir diese einfache Routine auch in GFA-Basic verwirklichen. Die benutzte XBIOS-Routine ist gleich, auch der Aufruf ist äquivalent zu C. Die Nutzung einer Xbios-Routine wurde notwendig, da man mit der normalen Basic-Syntax zwar eine Farbe neu definieren, den Farbwert jedoch nicht auslesen kann.

Die Prozedur 'Lese__normfarbe’ liest die Farbpalette in das Feld ’FELD%()’, wo sie global gespeichert wird. Zwischenzeitliche Farbänderungen lassen sich am Programmende durch die Prozedur Farbnormal’ zurücksetzen.

Zum Schluß möchte ich auf ein Update eingehen. Diejenigen, die mit dem MEGAMAX arbeiten, werden folgendes Problem eines Lesers kennen:

Wie ich festgestellt habe, funktionieren die Steuerzeichen innerhalb einer PRINTF-Anweisung des MEGAMAX nur unzureichend. PRINTF-Befehle, in denen folgende Steuerzeichen verwendet werden, werden nicht sofort ausgeführt bzw. erst am Ende des Programmes: ("\t, \b, \r, \f, \0, \, "). Eine Ausnahme bildet (\n).

Was ist dagegen zu tun?

Der Grund für den Fehler der PRINTF-Anweisung: Printf schreibt die Zeichen auf den Bildschirm, als würde es die Zeichen in eine Datei schreiben. Wie vielleicht bekannt sein dürfte, werden beim Schreiben in eine Datei die Zeichen erst dann ausgegeben, wenn der Puffer voll ist oder ein EOF ausgegeben wird. Ähnlich verhält es sich mit printf von MEGAMAX. Der Text wird erst dann ausgegeben, wenn genügend Zeichen vorhanden sind oder ein ’Carriage Return’ (’ N’) geschrieben wird. Wie jedoch kann man, ohne daß man ein ’n’ oder genügend Zeichen schickt, printf dazu bewegen, daß es Zeichen ausgibt? Ganz einfach! Es gibt einen Befehl, der sich FFLUSH() nennt. Er bewirkt, daß der momentane Pufferinhalt direkt auf den Bildschirm ausgegeben wird. Führt man nach jedem printfO einen fflush() durch, wird der Text auf den Bildschirm ausgegeben. Nun ist es wünschenswert, eine neue printf-Routine zu haben, die diesen fflush() automatisch durchführt. Dabei muß allerdings ein Eingriff in die alte Routine vorgenommen werden. Dies ist leider schwer zu beschreiben. APPLICATION SYSTEMS wird unsere Änderung übernehmen. Wir bedanken uns, daß wir diesen Update durchführen dürfen:

Ich hoffe, daß ich auch in diesem Monat einigen Programmierern geholfen und vielleicht etwas Entwicklungszeit gespart habe. Natürlich gilt noch immer der Aufruf, uns interessante Probleme, die vielleicht auch noch wenig angesprochen worden sind, mitzuteilen. Natürlich können Sie die ST-Ecke auch mit eigenen Lösungen bereichern! Bis zum nächsten Mal: VIEL SP ASS beim Programmieren!

(SH)

' program Farbrücksetzung 
' HS
Dim Farb%(15)               ! Hierhin werden die
'                           ! normalen Farbwerte gerettet
@Lese normfarbe             ! Liest die Farbwerte ein
@Klecks                     ! Zeichnet etwas auf den Schirm
Void Inp(2)
@Zufallsfarbe               ! Ändert die Farbpalette
Void Inp(2)
@Farbnormal                 ! Erzeugt wieder die normalen Farben
Void Inp(2)
'
Procedure Lesenormfarbe 
    Local Colornum%
    For Colornum%=0 To 15 
        Farb%(Colornum%)=Xbios(7,Colornum%,-1) And &H777 
        ' Print Hex$(Farb%(Colornum%)) ! Ausgabe der Farbwerte 
    Next Colornum%
Return
'
Procedure Zufalls_farbe 
    Local Colornum%
    For Colornum%=0 To 15 
        Void Xbios(7,Colornum%,Random(&H777))
    Next Colornum%
Return
'
Procedure Klecks 
    Local I 
    For I=0 To 15 
        Deffill I
        Pcircle 1*20,10,10 
    Next I 
Return
'
Procedure Farbnormal 
    Local Colornum%
    For Colornum%=0 To 15 
        Void Xbios(7,Co1ornum%,Farb%(Colornum%))
    Next Colornum%
Return

Listing 3:

#include <stdio.h> 
extern _fprintf(),fflush();

printf()
{
    asm
        {
        pea 8(A6) 
        pea _iob+20(A4) 
        jsr _fprintf 
        addq.l #8,A7 
        pea iob+20(A4) 
        jsr fflush(PC) 
        addq.l #4,A7 
        }
}

Listing 4:



Aus: ST-Computer 04 / 1987, Seite 20

Links

Copyright-Bestimmungen: siehe Über diese Seite