Powrót do spisu treści

Rozdział 10

OBSŁUGA MANIPULATORÓW

    W każdym komputerze ważną rolę pełnią manipulatory. Szczególnie ważne jest ich zastosowanie w komputerach domowych, gdzie służą do gier. Zasadniczo można wyróżnić dwa rodzaje manipulatorów stosowanych w sprzęcie komputerowym: manipulatory analogowe i manipulatory cyfrowe.

    Sygnałem z manipulatora analogowego jest napięcie. W najprostszych rozwiązaniach uzyskuje się to przez włączenie między bieguny zasilania komputera potencjometru. Rozwiązaniem bardziej skomplikowanym może być przetwornik zamieniający np. szybkość obrotową na napięcie. Do manipulatorów analogowych zalicza się potencjometry (paddle), myszy i tabliczki graficzne.

    Całkowicie odmienne jest działanie manipulatora cyfrowego. Zawiera on kilka styków, które mogą mieć tylko dwa stany - zwarty i rozwarty. Najczęściej spotykanym rozwiązaniem jest takie, w którym styk ma napięcie 5 V i jest zwierany do masy. Ten zespół styków jest dołączony do jednego z układów komputera, skąd położenie poszczególnych styków jest odczytywane jako stany bitów rejestru. Manipulatorem cyfrowym jest przede wszystkim joystick. Ponadto w manipulatorach analogowych stosuje się jeden lub dwa przyciski, których działanie jest oparte na zasadzie zwierania styków, a więc cyfrowe.

    Komputery Atari 400/800 miały po cztery gniazda manipulatorów. Do każdego z tych gniazd można było dołączyć joystick lub dwa potencjometry. W sumie daje to ogromną liczbę czterech joysticków lub ośmiu potencjometrów. Dało tu o sobie znać pochodzenie komputerów Atari od gier telewizyjnych. W nowych modelach XL i XE liczba gniazd została zredukowana do przyzwoitej liczby dwóch, lecz ze względu na zachowanie zgodności oprogramowania pozostały rejestry w pamięci RAM.

    Ponieważ manipulatory dwóch wymienionych wyżej typów mają zupełnie odmienne działanie, to są one obsługiwane przez różne układy komputera. Są to trzy układy, a nie dwa, jak można by przypuszczać, gdyż przyciski joysticków są obsługiwane oddzielnie.

10.1. Potencjometry

    Do dwóch gniazd manipulatorów można dołączyć w Atari cztery potencjometry (parami). Sygnał analogowy (napięcie) z gniazda jest przesyłany do układu POKEY i tam poddawany konwersji na postać cyfrową. Wykonuje tą operację specjalny przetwornik analogowo-cyfrowy (A/DC - Analog/Digital Converter).

    Przetworniki te sprawiają konstruktorom komputerów dużo kłopotów i to nie ze względów technicznych. Osiągnięcie nawet bardzo dużej dokładności nie stanowi żadnego problemu. Wymaga za to czasu. Doprowadzany sygnał jest kolejno porównywany z napięciami wzorcowymi o rosnących wartościach. Czym więcej napięć wzorcowych, tym dokładniej określone jest napięcie sygnału, ale także tym dłużej trwa pomiar. Największą trudnością jest znalezienie właściwego kompromisu pomiędzy dokładnością i szybkością przetwornika.

    Rozwiązanie zastosowane w Atari jest następujące: POKEY mierzy jednocześnie osiem sygnałów wejściowych (modele XL i XE wykorzystują tylko cztery z nich). Ponieważ wykonuje to co 1/50 sekundy (z częstotliwością synchronizacji obrazu - 50 Hz), to w tym czasie możliwe jest porównanie każdego sygnału z 228 napięciami wzorcowymi. Zamiana sygnału analogowego na cyfrowy daje więc w rezultacie wartości z zakresu od 0 do 228.

    Obliczone wartości mogą być odczytane z rejestrów POT0-7 (POTentiometr 0-7 - $D200-$D207). Niestety w języku maszynowym nie jest to proste (Atari Basic i większość języków wyższego poziomu sama wykonuje opisane niżej czynności). Rejestry te bowiem są jednocześnie licznikami sprzętowymi układu POKEY. Ich stan jest stopniowo zmniejszany, a po wyzerowaniu któregokolwiek wywoływane jest przerwanie IRQ (TIMER 1, 2 i 4), natomiast licznik jest ponownie ustawiany na 228 lub według zawartości rejestrów-cieni. Dzięki temu mogą one być wykorzystane do zliczania okresów czasu krótszych od częstotliwości obrazu (50 Hz, czyli 1/50 sekundy) - jest to opisane w poprzednim rozdziale.

    Prawidłowy odczyt stanu potencjometru umożliwiają dwa dodatkowe rejestry. Rejestr POTGO (POTentiometrs GOes - $D20B) uruchamia odczyt i konwersję sygnału wejściowego po wpisaniu do niego dowolnej wartości. Zakończenie zamiany sygnału analogowego na cyfrowy jest sygnalizowane skasowaniem bitu w rejestrze POTSTAT (POTentiometrs STATus - $D208). Każdemu bitowi tego rejestru (0-7) odpowiada potencjometr o tym samym numerze. Ustawienie bitu w POTSTAT oznacza więc, że wartość odpowiadającego mu potencjometru jest jeszcze obliczana i odczyt z POT... da nieprawidłowy wynik.

    Jeżeli znacznie ważniejsza od dokładności jest szybkość przetwarzania sygnału, to można ją zmienić poprzez rejestr SKCTL (Serial and Keyboard ConTroL - $D20F). Gdy bit 2 tego rejestru jest skasowany (stan normalny), to przetwarzanie sygnału trwa 20 milisekund, co odpowiada utworzeniu na ekranie 228 linii. Po ustawieniu tego bitu czas przetwarzania skraca się do 128 mikrosekund (dwie linie ekranu), jednak kosztem znacznego zmniejszenia dokładności pomiaru. Przy zmianie zawartości tego rejestru trzeba pamiętać, aby nie zmienić innych, wcześniej ustawionych bitów (normalnie wszystkie są skasowane).

    Stosowane do gier potencjometry (tzw. paddle) są ponadto wyposażone w przyciski - każdy paddle ma jeden przycisk. Sygnały z tych przycisków są dołączone do układu obsługującego joysticki (zob. rozdział następny). Odpowiadają one przesunięciom joysticka w lewo i w prawo:
        przycisk potencjometru 0 = joystick 0 w lewo
        przycisk potencjometru 1 = joystick 0 w prawo
        przycisk potencjometru 2 = joystick 1 w lewo
        przycisk potencjometru 3 = joystick 1 w prawo
        przycisk potencjometru 4 = joystick 2 w lewo
        przycisk potencjometru 5 = joystick 2 w prawo
        przycisk potencjometru 6 = joystick 3 w lewo
        przycisk potencjometru 7 = joystick 3 w prawo
    Potencjometry 4-7 oraz joysticki 2 i 3 występują tylko w komputerach Atari 400/800.

10.2 Joysticki

    Wykorzystanie joysticków jest znacznie prostsze ponieważ uzyskiwany jest z nich sygnał cyfrowy. W Atari obsługą joysticków zajmuje się układ scalony PIA (typowy układ 6520 lub 6820), a właściwie jego port A, gdyż PIA składa się z dwóch identycznych, niezależnych od siebie części. W modelach 400/800 port B obsługiwał dwa dalsze gniazda joysticków.

    Joystick ma cztery zasadnicze położenia (oprócz neutralnego): w lewo, w prawo, naprzód i wstecz. Pozostałe położenia są ich kombinacją. Do cyfrowego przedstawienia tych położeń wystarczą cztery bity, dla dwóch joysticków potrzebny jest więc jeden rejestr 8-bitowy. Takim rejestrem jest właśnie PORTA (PORT A - $D300). Jego poszczególne bity są przypisane następującym położeniom joysticka:
             bit 0 - joystick 0 naprzód
             bit 1 - joystick 0 wstecz
             bit 2 - joystick 0 w lewo
             bit 3 - joystick 0 w prawo
             bit 4 - joystick 1 naprzód
             bit 5 - joystick 1 wstecz
             bit 6 - joystick 1 w lewo
             bit 7 - joystick 1 w prawo
    Jak wynika z wstępnego opisu, normalnie wszystkie bity są ustawione. Wykonanie ruchu joystickiem powoduje skasowanie odpowiedniego bitu (lub bitów). Języki wyższego poziomu odczytują położenie joysticka z rejestrów-cieni - $0278 (joystick 0) lub $0279 (joystick 1). Odczyt położenia bezpośrednio z PORTA stosowany przeważnie w języku maszynowym wymaga natomiast wykonania kilku operacji. Przedstawia to poniższa procedura (analogicznie przeprowadzane jest przepisanie zawartości PORTA do rejestrów-cieni podczas przerwania VBLK).
            0100 ;Read Joystick's Movements
            0110 ;
            0120 PORTA = $D300
            0130 ;
            0140     *=  $0600
            0150 ;
            0160 ;Joystick 0
            0170 ;
            0180 JOY0 LDA PORTA
            0190     AND #$0F
            0200     RTS
            0210 ;
            0220 ;Joystick 1
            0230 ;
            0240 JOY1 LDA PORTA
            0250     LSR A
            0260     LSR A
            0270     LSR A
            0280     LSR A
            0290     RTS
    Dla uproszczenia późniejszego rozpoznania położenia joysticka zwykle po rozkazie LDA PORTA stosuje się jeszcze rozkaz EOR #$FF, który zamienia wartości wszystkich bitów na przeciwne. Teraz sprawdzenie konkretnego bitu jest wykonywane przez sekwencję rozkazów AND, BNE. Na przykład ruch w lewo wykrywany jest rozkazami AND #$04, BNE LEFT. Jeżeli bit 2 był ustawiony (po EOR #$FF oznacza to ruch w lewo), wykonywany jest skok do miejsca oznaczonego etykietą LEFT. Podobna metoda została zastosowana w procedurze zamieszczonej na stronie 194. Oczywiście można także sprawdzać po kilka bitów jednocześnie (np. AND #$06 wykrywa ruch w lewo i wstecz).

    Podobnie jak potencjometry, także każdy joystick wyposażony jest w przycisk (najczęściej jest ich kilka, lecz połączone są razem). Do wykrywania ich stanu służą rejestry układu GTIA (w PIA zabrakło miejsca) - TRIG0 i TRIG1 (TRIGger 0-1). W rejestrach tych bity 1-7 są niewykorzystywane, a stan przycisku jest sygnalizowany jedynie przez bit 0. Normalnie bit ten jest ustawiony, a jego skasowanie oznacza, że przycisk został wciśnięty.

    Wszystkie wymienione wyżej rejestry mają swoje kopie w pamięci RAM. Zawartości ich są uaktualniane podczas przerwania synchronizacji pionowej VBLK. Spojrzenie na mapę pamięci ujawnia, że rejestrów-cieni jest dwa razy więcej! Jest to pozostałość po modelach 400/800 utrzymana w celu zachowania zgodności oprogramowania. Przy przepisywaniu do rejestrów-cieni wartości dotyczące joysticka 0 są umieszczane w rejestrach joysticków 0 i 2, a joysticka 1 w rejestrach 1 i 3. Dotyczy to zarówno położenia joysticka, jak i stanu jego przycisku.

10.3. Wyjście z gniazd joysticków

    Układ PIA, do którego dołączone są gniazda joysticków, jest programowalnym układem wejścia/wyjścia. Można więc, przeprogramować go tak, aby zamiast odczytywać sygnał z tego gniazda, zapisywał go tam. Do tego celu trzeba wykorzystać rejestr PACTL (Port A ConTroL - $D302).

    Układ PIA ma wiele funkcji, które są sterowane między innymi przez PACTL. Trzeba więc uważać, aby nie wywołać zamieszania przy ingerowaniu w jego zawartość. Jedynym interesującym nas bitem tego rejestru jest bowiem bit 2. Gdy jest on ustawiony, rejestr PORTA działa jako rejestr przesyłania danych. Oznacza to, że sygnał doprowadzony z jednej strony rejestru jest przekazywany na drugą. Specjalnie używam enigmatycznych określeń stron: "jedna" i "druga", gdyż kierunek przesyłania jest zmienny. Skasowanie bitu 2 zamienia rejestr PORTA w rejestr porządkowania danych. Teraz wpisanie jakiejś wartości do tego rejestru ustawia kierunek przesyłania danych i to oddzielnie dla każdego bitu! Jeżeli bit wpisanej wartości jest skasowany, to ten bit rejestru PORTA będzie działał jako wejście (odczyt sygnału z gniazda joysticka). Ustawiony bit wpisanej wartości ustawia bit rejestru PORTA jako wyjście (zapis sygnału na gniazdo joysticka). Oto przykładowe wartości:
    $00 (bin 00000000) - wszystkie bity jako wejście
    $FF (bin 11111111) - wszystkie bity jako wyjście
    $F0 (bin 11110000) - gniazdo  joysticka  0  jako wejście, a
                         joysticka 1 jako wyjście
    $AA (bin 10101010) - bity  parzyste  (0,  2,  4  i  6) jako
                         wejście, bity nieparzyste (1, 3, 5 i 7) jako wyjście
    Po odpowiednim ustawieniu portu należy odtworzyć jego właściwą funkcję przez ustawienie bitu 2 w PACTL. Wszystkie te operacje można przeprowadzić nawet z poziomu Basica, a w języku maszynowym na przykład tak:
            0100 DIR = $xx  ;kierunek transmisji
            0110 PACTL = $D302
            0120 PORTA = $D300
            0130 ;
            0140     LDY #DIR
            0150     LDA PACTL
            0160     AND #$FB
            0170     STA PACTL
            0180     STY PORTA
            0190     ORA #$04
            0200     STA PACTL
            0210     RTS
    Sygnały wejściowe i wyjściowe w gniazdach joysticków odpowiadają standardowi TTL. Układ PIA jest dodatkowo separowany od gniazd rezystorami 2,2 kOhma, co zabezpiecza go przed przeciążeniem prądowym. Bardzo łatwe jest więc zaprojektowanie układu sterującego dowolnym urządzeniem, a nawet kilkoma urządzeniami. Można też zastosować komputer jako inteligentny sterownik, który będzie reagował na impulsy z urządzenia wysyłaniem do niego odpowiednich poleceń. Stworzono tu ogromne pole do popisu dla inwencji użytkownika. Warto jednak pamiętać, że komputer jest urządzeniem delikatnym i drogim. Przy stosowaniu napięć wyższych od 5 V należy stosować galwaniczne oddzielenie obwodów za pomocą transoptorów lub przekaźników. Niektórzy zalecają nawet oba sposoby jednocześnie i stopniowanie napięcia: 5 V, 12 V i dopiero 220 V.

    Zamiast podsumowania przedstawiam zaczerpnięty z miesięcznika "Atari User" program, który jest przykładem wykorzystania portu A (a jednocześnie przykładem własnej procedury obsługi przerwania).
            0100 ;Joystick Driver
            0110 ;for device "J:"
            0120 ;
            0130 ICAX1Z = $2A
            0140 ICAX2Z = $2B
            0150 BOOT? = $09
            0160 PACTL = $D302
            0170 PORTA = $D300
            0180 HATABS = $031A
            0190 ;
            0200     *=  $0600
            0210 ;
            0220 ;construct HATABS entry
            0230 ;
            0240     PLA
            0250 JINIT LDX #$00
            0260 NEXTENT LDA HATABS,X
            0270     BEQ TABENT
            0280     INX
            0290     INX
            0300     INX
            0310     CPX #$22
            0320     BCS NOROOM
            0330     JMP NEXTENT
            0340 NOROOM BRK
            0350 ;
            0360 ;now to put the entry in
            0370 ;
            0380 TABENT LDA #'J
            0390     STA HATABS,X
            0400     INX
            0410     LDA # <JDRIVER
            0420     STA HATABS,X
            0430     INX
            0440     LDA # >JDRIVER
            0450     STA HATABS,X
            0460     RTS
            0470 ;
            0480 ;vector table
            0490 ;
            0500 JDRIVER .WORD OPEN-1
            0510     .WORD CLOSE-1
            0520     .WORD GET-1
            0530     .WORD PUT-1
            0540     .WORD STATUS-1
            0550     .WORD SPEC-1
            0560     JMP INIT
            0570 ;
            0580 ;open routines
            0590 ;
            0600 OPEN LDA ICAX1Z
            0610     CMP #$0C
            0620     BEQ IOOP
            0630     CMP #$08
            0640     BEQ OPOP
            0650     CMP #$04
            0660     BEQ IPOP
            0670     LDY #$92
            0680     RTS
            0690 ;
            0700 ;open for INPUT/OUTPUT
            0710 ;
            0720 IOOP LDX ICAX2Z
            0730 PATCH LDA #$38
            0740     STA PACTL
            0750     STX PORTA
            0760     LDA #$3C
            0770     STA PACTL
            0780 OK  LDY #$01
            0790 INIT RTS
            0800 ;
            0810 ;open for OUTPUT only
            0820 ;
            0830 OPOP LDX #$FF
            0840     JMP PATCH
            0850 ;
            0860 ;open for INPUT only
            0870 ;
            0880 IPOP LDX #$00
            0890     JMP PATCH
            0900 ;
            0910 ;normal is INPUT, so...
            0920 ;
            0930 CLOSE JMP IPOP
            0940 ;
            0950 ;put a byte to port
            0960 ;
            0970 PUT STA PORTA
            0980     JMP OK
            0990 ;
            1000 ;get a byte from port
            1010 ;
            1020 GET LDA PORTA
            1030     JMP OK
            1040 ;
            1050 ;function not implemented
            1060 ;
            1070 STATUS
            1080 SPEC RTS
    Program ten instaluje w tabeli HATABS urządzenie o nazwie "J:". Może być ono otwarte do odczytu, zapisu lub odczytu i zapisu. W tym ostatnim przypadku można dowolnie ustalić, które styki będą wejściem, a które wyjściem. Po zainstalowaniu urządzenie można wykorzystywać z poziomu Basica. Dostępne są wtedy następujące instrukcje:
    OPEN #n,4,0,"J:" - otwarcie do odczytu
    OPEN #n,8,0,"J:" - otwarcie do zapisu
    OPEN #n,12,x,"J:" - otwarcie do zapisu i odczytu (x określa
                        bity wejściowe i wyjściowe)
    GET #n,A - odczyt bajtu
    PUT #n,A - zapis bajtu
    CLOSE #n - zamknięcie;  ustawia  PORTA do odczytu (normalny
               stan portu)
Zientara Wojciech: Mapa pamięci Atari XL/XE. Procedury wejścia-wyjścia, SOETO, Warszawa, 1988.