QBasic "myszowaty"


      Interfejs interpretatora QBasica może być obsługiwany myszą. Ale wśród poleceń języka próżno by szukać komend dla jej obsługi (chociaż jest joystick i pióro świetlne...). Microsoft w Pomocy do QB sugeruje: Zastosuj Microsoft Visual Basic for MS-DOS, który ma wbudowaną obsługę myszy. Otóż niekoniecznie. W Internecie można napotkać procedury pozwalające na użycie myszy w programach QB.

(Przedstawiony tu opis jest dość swobodnym przekładem tutoriala zamieszczonego na stronie http://qbasicnews.com/tutorials/tutor4.txt.)

W dość trudnej rzeczy, jaką jest asembler, jest jedna rzecz łatwa. PRZERWANIA (Interrupts). Używają ich niezliczone programy pod DOS-em. Program QBasic'a wywołuje przerwania cały czas, tylko tego nie widzimy i nie możemy ich kontrolować.

Aby użyć przerwania, trzeba wpisać do pamięci pewne informacje i wywołać numer przerwania. Na liście przerwań (np. http://www.htl-steyr.ac.at/~morg/pcinfo/hardware/interrupts/inte1at0.htm) znajdziemy, że przerwanie obsługujące mysz ma numer 33. Zanim się go wywoła, trzeba, wpisując odpowiednią liczbę do pamięci, przekazać komputerowi co chcemy zrobić myszą. Oto lista najważniejszych:

  • 0   Resetuje sterownik i odczytuje status
  • 1   Pokazuje mysz
  • 2   Ukrywa mysz
  • 3   Podaje współrzedne X,Y myszy i status przycisku
  • 4   Ustawia pozycję kursora myszy
  • 5   Dane o naciśnięciu klawisza myszy
  • 6   Dane o zwolnieniu klawisza myszy
  • 7   Określa zasięg poziomy   (MAX X)
  • 8   Określa zasięg pionowy   (MAX Y)

W asemblerze włączenie myszy wyglądałoby tak:

      MOV AX,1
      INT 33

Wstawiamy (MOV) 1 do pamięci (AX), wywołujemy przerwanie (INT) 33 i kursor myszy pokazuje się na ekranie. W asemblerze jest to bardzo proste, w QBASIC'u nieco bardziej zawiłe. Nie można użyć MOV i INT do wywołania... Trzeba użyć programu.

Program zawiera dwie procedury SUB i nie daje się skutecznie skopiować najprostszą metodą wprost z WWW. (W QB wychodzą straszne "schody", i - co gorsze - ginie w kopiowaniu druga procedura.) Trzeba zastosować kopiowanie z pośrednictwem pliku tekstowego:

  1. otwórz edytor tekstu (Notepad.exe);
  2. podświetl poniższy kod (zacznij tam, gdzie zaznaczono);
  3. wybierz Edycja i Kopiuj;
  4. otwórz nowy plik w edytorze;
  5. wybierz Edycja i Wklej;
  6. wybierz Plik i Zapisz jako...;
  7. w oknie "Zapisz jako" wybierz 'Zapisz jako typ: "Wszystkie pliki (*.*)";
  8. znajdź folder C:\ (lub jaki zechcesz; ten przykład będzie używał folder C:\ ...);
  9. w oknie "Nazwa pliku:" wpisz Mouse.Bas;
  10. kliknij "Zapisz...";
  11. wejdź do QBasic'a;
  12. załaduj c:\Mouse.bas;
  13. uruchom program;
  14. poruszaj i klikaj myszą, obserwuj, co się zmienia...
W większości przypadków to działa. Jeśli używasz QuickBasic 4.0 lub wyższy, musisz uruchomić QBasic spod DOSa, pisząc FOLDER:\qb /l
Ale najlepiej użyć oryginalny QBasic. A oto i kod:

'-------------| START programu myszy (Stąd zacznij kopiować) |------------

DEFINT A-Z
DECLARE SUB mouse (cx, dx, bx)
DECLARE SUB MousePointer (SW)
DIM SHARED a(9)                 'Set up array for code
DEF SEG = VARSEG(a(0))          'Get array segment (nnnn:    )
                                 '    (two 8 bit)
    FOR i = 0 TO 17                 'length of DATA to
       READ r                       'read
       POKE VARPTR(a(0)) + i, r     'into array/2 (nnnn:iiii) (one 8 bit)
    NEXT i                          'until 17

'**************************** Machine Code *******************************

DATA &HB8,&H00,&H00   :   ' mov  AX,[n]       [Swap code-(L),(H)] in AX
DATA &H55             :   ' push BP           Save BP
DATA &H8B,&HEC        :   ' mov  BP,SP        Get BP to c Seg
DATA &HCD,&H33        :   ' int  33           Interrupt 33
DATA &H92             :   ' xchg AX,[reg]     [Swap code-reg] in AX
DATA &H8B,&H5E,&H06   :   ' mov  BX,[BP+6]    Point to (variable)
DATA &H89,&H07        :   ' mov  [BX],AX      Put AX in (variable)
DATA &H5D             :   ' pop  BP           Restore BP
DATA &HCA,&H02,&H00   :   ' ret  2            Far return

'SCREEN 12
'****************************** Mouse set up ****************************
           
                CALL MousePointer(0)      'Reset mouse and
                CALL MousePointer(1)      'turn pointer on
                CALL MousePointer(3)      'Get coordinates

'****************************** P R O G R A M ***************************

DO                             'Tutaj wstaw swoj kod
 CALL mouse(cx, dx, bx)
  LOCATE 1, 1: PRINT dx; cx; bx
LOOP UNTIL INKEY$ = CHR$(27)   'Koniec twojego kodu
END

SUB mouse (cx, dx, bx)
         
           POKE VARPTR(a(4)), &H92           'Swap code,Get CX setup
          CALL absolute(cx, VARPTR(a(0)))     'Run Code
              cx = cx / 8                     'Adjust 25x80
           POKE VARPTR(a(4)), &H91           'Swap code,Get DX setup
          CALL absolute(dx, VARPTR(a(0)))     'Run Code
              dx = dx / 8                     'Adjust 25x80
           POKE VARPTR(a(4)), &H93           'Swap code,Get BX setup
          CALL absolute(bx, VARPTR(a(0)))     'Run Code

                                   'Uwaga :
                                   'Usuń /8
                                   'w trybach graficznych. 
END SUB

SUB MousePointer (SW)
         
           POKE VARPTR(a(0)) + 1, SW         'Swap code,Set AX = (SW)
          CALL absolute(c, VARPTR(a(0)))     'Run Code

                                          'Note:
                                             'SW = 0-reset
                                             'SW = 1-on
                                             'SW = 2-off
                                             'SW = 3-coordinates
END SUB

'-------------------| THE END (tu odetnij) |-----------------------------

Program w tej postaci jest przystosowany do pracy w trybie tekstowym. Fragment

  LOCATE 1, 1: PRINT dx; cx; bx
wyświetla w lewym górnym rogu ekranu wartość tych trzech zmiennych, które pokazują odpowiednio: dx - współrzedną poziomą (kolumnę), cx - współrzedną pionową (wiersz) oraz bx - stan klawiszy myszy. dx i cx zmieniają się podczas poruszania myszą, bx reaguje na naciśnięcie klawisza myszy: 1 - naciśnięty lewy klawisz, 2 - naciśnięty prawy, 3 - naciśnięte obydwa razem(!). Wyjście z programu następuje po naciśnięciu klawisza [Esc] (LOOP UNTIL INKEY$ = CHR$(27) ).
    Warto zauważyć, że współrzędne lewego górnego rogu ekranu dla polecenia LOCATE to 1,1, zaś dla myszy 0,0. Tą różnicę o 1 trzeba będzie uwzględnić, wykorzystując zmienne dx i cx w swoich programach.

Zwróć uwagę na fragmenty zaznaczone na czerwono. Znakiem komentarza zablokowane jest polecenie SCREEN 12, dzieki czemu program pracuje teraz w trybie tekstowym. Jego odblokowanie spowoduje pracę w tym trybie graficznym. Aby prawidłowo wyświetlał współrzędne, trzeba jeszcze zablokować (REM lub ') dzielenia przez 8 w SUB mouse:


Wybierz kolejno:
  • Viev
  • SUBs...
  • mouse
  • Edit in Active
W procedurze zablokuj znakiem komentarza wiersze opisane "Adjust 25x80".


Fragment zaznaczony "Tutaj wstaw swój kod" to miejsce na wpisanie programu, w którym mysz ma być wykorzystywana. Program tutaj przedstawiony stanowi jakby "ramkę" dla niego.

A to przykład wykorzystania w trybie graficznym. Po odblokowaniu SCREEN 12 i zablokowaniu dzielenia przez 8, w miejscu poniżej LOCATE 1, 1: PRINT dx; cx; bx i powyżej LOOP UNTIL INKEY$ = CHR$(27) wklej następujący kod:

 LINE (50, 50)-(80, 70), 15, B
 IF bx = 1 THEN
  IF dx > 50 AND dx < 80 THEN
   IF cx > 50 AND cx < 70 THEN
    LOCATE 2, 1: PRINT "Klikasz w prostokacie, w miejscu "; dx; ","; cx
   END IF
  END IF
 END IF

Ten kod rysuje ne ekranie prostokąt, który można kliknąć. Wówczas program wypisze odpowiedni komunikat.

I do kompletu jeszcze końcowe uwagi z wykorzystywanego tutoriala:

  1. Jeżeli chcesz wyłączyc widoczność myszy, bo możesz wstawić swój własny kursor na pozycji myszy, zastosuj polecenia
    • CALL MousePointer(2)
      CALL MousePointer(3)

    Przy tym możesz nadal wskazywać współrzedne myszy, ale nie będziesz jej widział. Jest to dobre, jeśli chcesz stworzyć swój własny kursor myszy bez majstrowania w asemblerze i bitmapach.

  2. Jeżeli coś narysujesz nad myszą i następnie nią poruszysz, możesz zauważyć, że mysz przedziera się przez tło i rujnuje to, co narysowane. Aby to naprawić, zrób następująco:
      CALL MousePointer(2)     'Wyłącza wskaźnik myszy
      'Narysuj swój obraz
      CALL MousePointer(1)     'Przywraca wskaźnik
      CALL MousePointer(3)     'Przechwytuje współrzędne wskaźnika
    Powinno to rozwiązać ten problem.

  3. Polecenia CALL MousePointer(x)
    • 0 - Resetuje mysz
      1 - włącza mysz
      2 - wyłącza mysz
      3 - pobiera współrzędne myszy

Eksperymentując z tym programem, nie zauważyłem zjawiska opisywanego pod 2). Na ekranie z prostokątami wypełnionymi kolorem wskaźnik myszy nie poruszanej ginął pod plamą prostokąta (także przy kolorze białym), zaś poruszany - migotał na jego tle. Tak działo się, jeżeli polecenia rysujące prostokąty znajdowały się wewnątrz pętli. Jeżeli zaś prostokąt został narysowany przed pętlą, to wskaźnik pokazywał się na jego tle w całej okazałości, bez żadnych zakłóceń.


D.A.M. Deger (Damian Gawrych) listopad 2006
http://deger.republika.pl