Alles ueber den Gameport des PC:

Aufbau und Ansteuerung:

Der Gameport wird bei XTs normalerweise ueber den Port 200h und bei ATs
ueber den Port 201h angesprochen.
Viele der modernen Karten im AT lassen sich aber unter beiden Adressen
ansprechen. Damit wird gewaehrleistet, dass auch Programme, welche fuer den
XT geschrieben wurden, auf dem AT problemlos laufen.

Der Gameport (bzw. der Spieleadapter) besitzt einen Puffer, in dem der
Ausloesezustand der Joystick-Tasten abgelegt wird, und einen
Kippstufenbaustein fuer die Ermittlung der Position des Joysticks.

Der Adapter ist lediglich ueber die unteren acht Bits des Datenbusses, die
zehn niederwertigen Bits des Adressbusses und die Steuerleitungen #IOR und
#IOW mit dem PC-Systembus verbunden. Auf der Rueckseite weist der Adapter
eine 15-polige Buchse auf, an die maximal zwei Spielekonsolen angeschlossen
werden koennen.

Die Belegung:

Pin          Belegung

 2          1. Taste Joystick A (TA1)
 3          X-Potentiometer Joystick A (AX)
 6          Y-Potentiometer Joystick A (AY)
 7          2. Taste Joystick A (TA2)
 10         1. Taste Joystick B (TB1)
 11         X-Potentiometer Joystick B (BX)
 13         Y-Potentiometer Joystick B (BY)
 14         2. Taste Joystick B (TB2)
 1,8,9,15   Versorgungsspannung Vcc (+5V)
 4,5,12     Masse GND (0V)

Jeder der beiden Joysticks besteht aus zwei Potentiometern mit einem
Widerstandswert zwischen 0 und 100 kOhm, die senkrecht zueinander
angeordnet sind und die X- bzw. Y-Position des Joysticks angeben. Ferner
sind an jedem Joystick bis zu zwei Tasten angebracht. Normalerweise sind
diese offen, und die entsprechenden Leitungen werden durch die Verschaltung
im Joystick auf einen hohen Pegel gezogen (siehe Anschlussdiagramm).
Bei einer offenen Taste erhaelt man also eine 1 im entsprechenden Bit des
Statusbytes. Wird die Taste dann gedrueckt, wird aus der 1 eine 0.

Statusbyte des Joysticks:

 Bit    7    6    5    4    3    2    1    0
       TB2  TB1  TA2  TA1   BY   BX   AY   AX

       TB2, TB1, TA2, TA1 : Zustand der Tasten (1 = offen, 0 = gedrueckt).
       BY, BX, AY, AX     : Zustand der Kippstufe entsprechend dem
                            zugehoerigen Potentiometer.

Den Zustand 'gedrueckt' oder 'offen' der Tasten koennen sie ueber einen
IN-Befehl an die Portadresse sehr einfach ermitteln. Der Spieleadapter gibt
ihnen ein Datenbyte mit obigem Aufbau zurueck. Das hoeherwertige Nibble zeigt
den Zustand der Tasten. Da der Adapter keine IRQ-Leitung verwendet und somit
auch keinen Interrupt ausloest, muessen sie das Statusbyte fortlaufend
abfragen, um den Status zu ermitteln. Der Spieleadapter arbeitet also nur im
Polling-Modus.

 Beispiel : Ermitteln, ob die 2. Taste von Joystick A gedrueckt wurde

   MOV  DX,201h    ; Adresse (fuer ATs) des Spieleadapters in DX laden
   IN   AL,DX      ; Statusbyte in AL einlesen.
   TEST AL,20h     ; pruefen, ob TA2 gesetzt ist.

Etwas umstaendlicher ist es, die Stellung des Joysticks abzufragen. Sie
muessen den aktuellen Widerstandswert des Potentiometers ermitteln. Hierzu
dient der Kippstufenbaustein im Spieleadapter. Wenn sie einen OUT-Befehl
ueber die Portadresse mit einem beliebigen Wert ausgeben, wird im
Kippstufenbaustein eine monostabile Kippstufe gestartet. Die Werte der
vier niederwertigen Bits BY, BX, AY und AX im Statusbyte werden dann auf 1
gesetzt. Die Kippstufe besteht im wesentlichen aus einem Kondensator, der
sich ueber einen 2,2 kOhm-Widerstand auf der Adapterkarte und das
entsprechende Joystick-Potentiometer entlaedt. Ist die Kondensatorspannung
durch den Entladevorgang unter einen bestimmten Schwellenwert abgesunken,
gibt der Kippstufenbaustein statt einer 1 eine 0 aus. Je nach
Potentiometerwiderstand dauert das mehr oder weniger lange. Der exakte
Zusammenhang lautet

   Zeitspanne = 24,2E-6 s + 0,011E-6 s * Widerstand [Ohm]

oder

   Widerstand [Ohm] = (Zeitspanne - 24,24E-6 s) / 0,011E-6 s

Die Zeitspanne kann also zwischen 24,2E-6 s fuer die Potentiometerstellung
0 Ohm und 1124E-6 s bei 100 kOhm schwanken. Um den Widerstandswert der
einzelnen Potentiometer zu ermitteln, geben sie also zunaechst ein
beliebiges Datenbyte auf den Port aus, um die Kippstufe zu starten. Die
entsprechenden Bits im Statusbyte wechseln dann auf 1. Anschliessend
ermitteln sie fortlaufend anhand des Statusbytes durch staendiges Auslesen
des Ports, ob der Wert des gewuenschten Bits auf 0 gefallen ist. Die
Zeitspanne koennen sie z.B. durch Auslesen von Zaehler 0 des Timer-Chips
PIT 8253/8254 vor dem ersten und nach dem letzten IN-Befehl ermitteln.
Bei Zaehler 0 findet nur alle 55 ms ein Wrap-around des Zaehlers statt. Die
Zeitspanne entsprechend einer Potentiometereinstellung ist also stets
wesentlich kleiner als eine Zykluszeit des Zaehlers. Natuerlich koennen sie
auch Zaehler 2 des PIT mit geeigneten Zaehlwerten laden und zur Erfassung der
Zeitspanne nutzen. Aus der ermittelten Zeit koennen sie mit Hilfe des oben
angegebenen Zusammenhangs dann den Widerstandswert und somit die Stellung
des Joysticks bestimmen.

 Beispiel : Widerstandswert des Potentiometers AX entsprechend der
            X-Auslenkung von Joystick A ermitteln

   MOV  DX,201h    ; Adresse (fuer ATs) des Spieleadapters in DX laden.
   OUT  DX,AL      ; beliebigen Wert auf den Port schreiben, um Kippstufe
                   ; zu starten.
   MOV  AL,0       ; Zaehler-Latch-Befehl bezueglich Zaehler 0 in AL laden.
   OUT  43h,AL     ; Zaehler-Latch-Befehl an Steuerregister des PIT ausgeben.
   IN   AL,40h     ; niederwertiges Zaehlerbyte in AL einlesen.
   IN   AH,40h     ; hoeherwertiges Zaehlerbyte in AH einlesen,
                   ; damit enthaelt AX den aktuellen 16-Bit-Zaehlerstand.
   MOV  BX,AX      ; Zaehlerstand nach BX uebertragen.

   check_status:
   IN   AL,DX      ; Statusbyte aus Port lesen.
   TEST AL,01h     ; pruefen, ob Bit AX immer noch gesetzt ist.
   JZ check_status ; wenn Bit AX gesetzt, nochmals pruefen.

   MOV  AL,0       ; Zaehler-Latch-Befehl bzueglich Zaehler 0 in AL laden.
   OUT  43h,AL     ; Zaehler-Latch_Befehl an Steuerregister des PIT ausgeben.
   IN   AL,40h     ; niederwertiges Zaehlerbyte in AL einlesen.
   IN   AH,40h     ; hoeherwertiges Zaehlerbyte in AH einlesen,
                   ; damit enthaelt AX den aktuellen 16-Bit-Zaehlerstand.
   MOV  CX,AX      ; Zaehlerstand nach CX uebertragen.


   ............    ; Jetzt aus BX und CX Zaehlerdifferenz, Zeitwert und
                   ; Widerstandswert berechnen.


Ferner haben sie neben einem direkten Registerzugriff auch die Moeglichkeit
den Status der Tasten und der Kippstufe ueber das BIOS abzufragen. Dazu
dient die Funktion 84h des INT 15h:

Verfuegbar auf Rechnern : XT mit einem BIOS-Datum nach 08.11.82, AT, XT286
                         und PS/2.

Register beim Aufruf   : AH = 84h
                         DX = Unterfunktion
                              0000h liest die Joystick-Tasten aus.
                              0001h liest die Joystick-Position aus.

Register bei Rueckkehr : CF gesetzt bei einem Fehler
                            AH = Status
                                 80h ungueltiges Kommando (PC, PCjr)
                                 86h Funktion nicht unterstuetzt (andere)
                         CF geloescht, wenn erfolgreich
                            AL Bits 7..4 = Tasten fuer Unterfunktion 0000h
                            AX = X-Position von Joystick A fuer Unterfunktion
                                 0001h
                            BX = Y-Position von Joystick A
                            CX = X-Position von Joystick B
                            DX = Y-Position von Joystick B

Wenn kein Spieleadapter installiert ist, gibt Unterfunktion 0000h AL=00h
(alle Schalter offen) zurueck und Unterfunktion 0001h AX=BX=CX=DX=0000h.
Diese Antwort erhaelt man auch, wenn zwar ein Spieleadapter installiert,
jedoch kein Joystick angeschlossen wurde.
Mittels dieser Funktion laesst sich also nur sicher unterscheiden, ob sie
ueberhaupt unterstuetzt wird oder nicht.
Bei einem Joystick mit 250 kOhm-Potentiometern ergeben sich normalerweise
Werte zwischen 0000h und 01A0h.

Die Moeglichkeit herauszubekommen, ob ein Spieleadapter installiert ist,
auch wenn kein Joystick angeschlossen wurde, existiert leider nicht, da in
diesem Fall das Statusbyte immer den Wert FFh liefert. Dieser Wert wird von
allen nicht belegten Speicherstellen im PC zurueckgeliefert, da im PC alle
Leitungen des Datenbusses standardmaessig auf High gezogen werden.

Anschlussdiagramm:


           Joystick A                        Joystick B
     .........................  ____    ........................
     .                       . |    \   .                      .
     .  +-----+------------------1   \  .                      .
     .  |     |       _#_    . |   9-------------------+----+  .
     .  |    +-+   +--o o--------2    | .    _#_       |    |  .
     .  |    | |   | Taste 1 . |   10--------o o--+   +-+   |  .
     .  |    |X|<----------------3    | . Taste 1 |   | |   |  .
     .  |    | |   |         . |   11---------------->|X|   |  .
     .  |    +-+   +-------------4    | .         |   | |   |  .
     .  |     |    |         . |   12-------------+   +-+   |  .
     . +-+    o    |         . | 5    | .         |    |    |  .
     . | |         |         . |   13----------------+ o   +-+ .
     . |Y|<----------------------6    | .    _#_  |  |     | | .
     . | |         |  _#_    . |   14--------o o--+  +---->|Y| .
     . +-+         +--o o--------7    | . Taste 2          | | .
     .  |            Taste 2 . |   15 | .                  +-+ .
     .  o                    . | 8   /  .                   |  .
     .                       . |____/   .                   o  .
     .........................          ........................


Ein besonderer Effekt tritt auf, wenn zwar ein Spieleadapter im PC
vorhanden ist, aber kein Joystick angeschlossen wurde. Dann betraegt der
Widerstandswert der nicht vorhandenen Potentiometer aus der Sicht des
Spieleadapters quasi unendlich. Praktisch heisst das, wurde die
Kippstufe einmal aktiviert um die Position des Joysticks zu bestimmen,
kommt sie nie zu einem Ende. Die Bits im Statusbyte bleiben fuer einen
langen Zeitraum auf 1 stehen.
Will man also wissen, ob ein Joystick angeschlossen ist, so versucht man
einfach dessen Position zu bestimmen. Ist das nicht innerhalb eines
kurzen Zeitraumes moeglich, fallen die Bits im Statusbyte also nicht auf
0 zurueck, so kann man davon ausgehen, dass kein Joystick angeschlossen
ist. Die genaue Zeit haengt von der jeweiligen Geschwindigkeit des
Programms ab. Programmiert man in Maschinensprache, so stellt sich bereits
nach einigen Millisekunden heraus, ob ein Joystick da ist, oder nicht.
Wie oben schon erwaehnt liefert diese Methode aber nur dann ein sinnvolles
Ergebnis, wenn man sich absolut sicher ist, dass ueberhaupt ein Spieleadapter
installiert ist. Ansonsten ist diese Methode voellig sinnlos.

Im Prinzip laesst sich der Spieleadapter auch fuer andere Zwecke als den
Anschluss eines Joysticks verwenden. Es lassen sich alle moeglichen
Widerstandswerte verwenden, wobei der Wert nicht auf den Bereich von 0
bis 100 kOhm begrenzt ist. Ueber die vier Anschluesse fuer die Tasten laesst
sich - bei entsprechender Hardware - auch eine Datenuebertragung von einem
externen Geraet in den PC aufbauen. Dabei koennen immer 3 Bits auf einmal
uebertragen werden. Das 4.te Bit fuehrt dann ein Clock-Signal, welches die
Uebertragung der 3-Bit-Pakete synchronisiert. Natuerlich ist die
Datenuebertragung nur uni-direktional, da der PC die Bits fuer die Tasten
nicht selber veraendern kann.
Soll ein externes Geraet einfach nur bestimmte diskrete Zustaende anzeigen,
dann koennen, je nach Zustand, verschiedene fest vorgegebene
Widerstandswerte in den Stromkreis geschaltet werden. Waehlt man die
Differenz zweier aufeinanderfolgender Widerstandswerte nicht zu gering,
so kann der PC die einzelnen Werte sicher unterscheiden.

Jetzt folgt noch ein kleines Pascalprogramm, um den Spieleadapter und die
Joysticks einmal testen zu koennen. Ich habe es mit Turbo-Pascal 6.0
geschrieben. Es sollte aber auch mit anderen Versionen oder anderen
Pascal-Compilern laufen. Eventuell muss dann der port-Befehl angepasst
werden.

Um Platz zu sparen musste ich das Programm leider etwas unuebersichtlich
strukturieren. Sorry!

Falls das Programm auf einem XT nicht laeuft, bitte die Adresse des
Spieleadapters auf 200h aendern.

program gameport;

{
  (c) 1993, by Michael Nicolai. This program is Public Domain.
}

uses CRT, DOS;

const
 Adresse : word = $201;  { Adresse des Spieleadapters. }

var
 regs   : Registers;
 status : byte;
 ja, jb : byte;
 xa, ya : word;
 xb, yb : word;

begin
 clrscr;

 { Testen, ob die Funktion 84h des INT 15h unterstuetzt wird. }

 regs.ah := $84; regs.dx := $0001; intr($15, regs);
 if ((regs.flags and FCarry) <> 0) then
 begin
  writeln('Das BIOS dieses Computers unterstuetzt die Funktion des INT 15h');
  writeln('zum Auslesen des Joysticks nicht!'); writeln;
 end;

 { Testen, ob ein Spieleadapter installiert und ein Joystick
   angeschlossen wurde. }

 for xa := 1 to 32000 do   { Eine Zeitlang das Statusbyte einlesen. }
  status := port[Adresse];
 if (status <> $FF) then begin
  writeln('Es ist ein Spieleadapter installiert!'); writeln; end
 else begin
  writeln('Entweder ist kein Spieleadapter installiert, oder es wurde');
  writeln('kein Joystick angeschlossen!'); halt; end;

 { Testen, wieviele Joysticks angeschlossen sind. }

 writeln('Suche Joysticks. Bitte warten....');
 ja := 0; jb := 0;  { Annahme: Kein Joystick vorhanden. }
 port[Adresse] := 0;  { Den Kippstufenbaustein starten. }
 for xa := 1 to 65000 do
 begin
  status := port[Adresse];
  if (((status and 1) = 0) or ((status and 2) = 0)) then begin
   ja := 1; gotoxy(1, 7); writeln('Joystick A gefunden!'); end;
  if (((status and 4) = 0) or ((status and 8) = 0)) then begin
   jb := 1; gotoxy(1, 8); writeln('Joystick B gefunden!');
   if (ja = 1) then xa := 65000;    { Schleife beenden. }
  end; end;
 if ((ja = 0) and (jb = 0)) then
 begin
  writeln('Fehler beim Zugriff auf den Spieleadapter!');
  writeln('Entweder ist der Kippstufenbaustein auf Ihrem Spieleadapter');
  writeln('defekt, oder Sie haben ein Geraet mit einem zu grossen');
  writeln('Widerstand fuer die Potentiometer angeschlossen.'); halt; end;

 { Die Joysticks auslesen und die Werte darstellen. }

 repeat
  if (ja = 1) then  { Joystick A. }
  begin
   xa := 0; ya := 0; port[Adresse] := 0;  { Kippstufe starten. }
   repeat
    status := port[Adresse];
    if ((status and 1) = 1) then xa := xa + 1;
    if ((status and 2) = 2) then ya := ya + 1;
   until (((status and 1) = 0) and ((status and 2) = 0));
   gotoxy(1, 10); writeln('Joystick A:'); writeln;
   write('Taste 1: ');
   if ((status and 16) = 16) then writeln('offen    ')
    else writeln('gedrueckt');
   write('Taste 2: ');
   if ((status and 32) = 32) then writeln('offen    ')
   else writeln('gedrueckt');
   writeln('X-Pos  : ', xa, '   '); writeln('Y-Pos  : ', ya, '   ');
  end;
  if (jb = 1) then  { Joystick B. }
  begin
   xb := 0; yb := 0; port[Adresse] := 0;  { Kippstufe starten. }
   repeat
    status := port[Adresse];
    if ((status and 4) = 4) then xb := xb + 1;
    if ((status and 8) = 8) then yb := yb + 1;
   until (((status and 4) = 0) and ((status and 8) = 0));
   gotoxy(1, 18); writeln('Joystick B:'); writeln; write('Taste 1: ');
   if ((status and 64) = 64) then writeln('offen    ')
   else writeln('gedrueckt');
   write('Taste 2: ');
   if ((status and 128) = 128) then writeln('offen    ')
   else writeln('gedrueckt');
   writeln('X-Pos  : ', xb, '   '); writeln('Y-Pos  : ', yb, '   ');
  end;
 until (keypressed);
end.

