ZX Spectrum > PROGRAMOWANIE

Przerwania

<< < (2/5) > >>

Pyza^Illusion:
Taki artykuł się kiedyś pojawił w ZX LAND :p

Jest też na stronie Yarka: http://yarek.com/forum/index.php?topic=64.0

Tutaj kopia.

-----------------------------------------------------------------------------------------------------

Przerwania w trybie IM2

Co to jest przerwanie (interrupt)? Jest to cykliczne (lub po spełnieniu określonych warunków) wywołanie podprogramu niezależnie od aktualnie wykonywanych operacji. Następnie po wykonaniu odpowiedniego podprogramu powrót do właściwej pracy bez naruszenia programu głównego.

ZX Spectrum posiada trzy tryby przerwań. IM0 - przerwania zarezerwowane dla podprogramów RST (0,8,16,24,32,40,48,56), IM1 - przerwania zarezerwowane dla podprogramów RST 56. Te tryby w zasadzie nas nie interesują.
IM2 - to przerwania dowolnego wykorzystania dla urządzeń zewnętrznych i programów umieszczonych w pamięci, którymi to właśnie się szerzej zajmiemy.

Na początek warto wiedzieć, że przerwanie występuje równo co 1/50 sekundy i zaczyna się w momencie rozpoczęcia wyświetlania zawartości ekranu (czyli początek tzw. ramki).
W momencie przerwania następuje zapamiętanie na stosie adresu instrukcji do wykonania, gdy już powrócimy z przerwania.
Starszy bajt adres skoku do obsługi przerwania wskazuje rejestr I. Młodszy zależy od stanu szyny krawędziowej. Generalnie, gdy nic nie jest podpięte do szyny wartość ta powinna wynosić 255. Jeżeli do rejestru I zadamy wartość #BE, to w momencie przerwania zostanie pobrana z tablicy wektorów zaczynającej się od adresu #BE00 dwubajtowa liczba, która zawierać będzie adres pod który nastąpi skok. Tablica wektorów zawsze powinna zaczynać się od tzw. strony kodowej (#BF00, #C200) i zawierać 257 bajtów wypełnionych jednakową wartością. Jeżeli wypełnimy tablicę wartością #BF, to adresem, pod który nastąpi skok, zawsze będzie #BFBF. Tam też należy umieścić procedurę obsługi przerwania.
Ponieważ główny program zostaje przerwany w różnych momentach jego wykonywania, podprogram (wykonywany na przerwaniach) może zniszczyć jego aktualne rejestry. Po wyjściu z przerwania może nastąpić więc zawieszenie komputera lub nawet reset, gdyż wartości rejestrów mogły ulec zmianie. Aby temu zapobiec, warto w obsłudze przerwania zapamiętać aktualny stan rejestrów na stosie (komendy PUSH nn, gdzie nn jest dowolną parą rejestrów np. HL, BC, AF...). Dopiero wtedy wykonujemy podprogram np. odgrywanie muzyki, scrolla, zegarek itp. Po wykonaniu podprogramów na przerwaniach należy odtworzyć zapamiętane rejestry (komendy POP nn). UWAGA! Należy pamiętać o odwrotnej kolejności zdejmowania ze stosu zawartości rejestrów w stosunku do ich zapamiętywania. Wyjście z przerwania następuje po instrukcji RET (jak w zwykłym podprogramie), co powoduje zdjęcie ze stosu adresu instrukcji do wykonania zapamiętanej w momencie przerwania.
Należy jeszcze zadbać o możliwość włączania i wyłącznia przerwań w dowolnym momencie. Obie opcje znajdują się na końcu listingu.


--- Kod: ---; obsługa przerwań
      ORG   #BFBF
; zapamiętanie aktualnych wartości rejestrów
      PUSH  IY
      PUSH  IX
      PUSH  HL
      PUSH  DE
      PUSH  BC
      PUSH  AF
; wymiana rejestrów na alternatywne
      EXX
      EX    AF,AF'
; zapamiętanie rejestrów alternatywnych
      PUSH  HL
      PUSH  DE
      PUSH  BC
      PUSH  AF
        . . .

; miejsce na skoki do podprogramów - 16 bajtów
; max 5 x CALL

        . . .
; przywrócenie wartości rejestrów alternatywnych sprzed przerwania
      POP   AF
      POP   BC
      POP   DE
      POP   HL
; wymiana rejestrów pomiędzy alternatywnymi i zwykłymi
      EX    AF,AF'
      EXX
; przywrócenie wartości rejestrów
      POP   AF
      POP   BC
      POP   DE
      POP   HL
      POP   IX
      POP   IY
; wyjście z przerwania
      EI
      RET

; tworzenie tablicy wektorów dla przerwań
      ORG   #BFE9
INIT  LD    A,#BF
      LD    HL,#BE00
      LD    B,#00
LOOP  LD    (HL),A
      INC   HL
      DJNZ  LOOP
      LD    (HL),A

; włączenie przerwań IM 2
      ORG   #BFF5
ON    LD    A,#BE
      LD    I,A
      IM    2
      RET

; wyłączenie przerwań IM 2 - włączenie IM 1
      ORG   #BFFC
OFF   EI
      IM    1
      RET
--- Koniec kodu ---

#BF = 191
#BE00 = 48640
#BFBF = 49087
#BFD9 = 49113
#BFE9 = 49129
#BFF5 = 49141
#BFFC = 49148

Od adresu #BF01 do #BFBE (pomiędzy końcem tablicy, a początkiem obsługi przerwań) pozostaje pusty obszar dokładnie 190 bajtów do wykorzystania. Można tam umieścić np. procedurkę wymazywania ekranu, obsługę klawiatury lub wykorzystać w dowolny inny sposób.

Chcąc stworzyć tablicę wektorów przerwań i włączyć przerwania wykonujemy USR (CALL) 49129, samo włączenie przerwań (gdy tablica już jest) USR (CALL) 49141 oraz USR (CALL) 49148 w przypadku wyłączenia przerwań.

Dlaczego na obsługę przerwań wybrałem akurat "środek" pamięci? Druga ćwiartka pamięci (adresy od 16384 do 32767) jest to tzw. pamięć wolna, tzn. odczyt z tych komórek jest normalny, natomiast zapis do nich jest spowalniany przez układ ULA, który dodatkowo śmieci ekran dziwnymi krzaczkami (zwanymi także flickersami lub artefaktami) jeżeli w rejestrze I jest wartość mniejsza niż 128 (#80). Czwarta ćwiartka natomiast (49152-65535) może być wykorzystywana przez podprogramy zmieniające banki pamięci (ZX128kB), więc gdy umieścimy obsługę w tamtym obszarze i zmienimy bank może to doprowadzić do zawieszenia lub restartu komputera.

Warto jeszcze wiedzieć, że zwykle programy wykonywane na przerwaniach posiadają tzw. inicjację i często skompilowane np. od adresu 40000 pierwsze 3 bajty zajmuje skok JP adres_inicjacji, w celu ustawienia wartości początkowych procedury. W przypadku scrolla może być to np. ustawienie początku tekstu, w muzyce - początek utworu, wyciszenie dźwięku itp. Np. w skompilowanej od adresu 40000 muzyce z Sound Tracker'a inicjacja znajduje się pod 40000 natomiast wywołanie odgrywania muzyki pod 40006, czyli 6 bajtów dalej.

Abrimaal:
Procedura działa prawidłowo, dzięki Wam.
To jeszcze nie jest finalna versja gry, dużo jeszcze do zrobienia.
Jednak coś jest nie tak w grze.
Pousuwałem z kodu wszystkie DI,
oraz dźwięki na beeper (bardzo zwalniały i zagłuszały muzykę).

Zostawiłem tylko początkowy sygnał, ponieważ pomimo usunięcia DI,
W momencie, gdy zmieniają się kolory tła, muzyka się zatrzymywała.
Chciałem jeszcze zostawić odgłos rozwalenia przeciwnika,
lecz zamieniłem to na efekt na border (XOR 16 na XOR 3, a po tym OUT 254,A)

I tutaj pojawia się problem - zarówno w oryginale (ze strzałem), jak i po zmianie efektu na border,
w tym miejscu muzyka często się zatrzymuje, włącza się dopiero po zabiciu drugiego wroga,
lub zabiciu samego siebie.
Jakie instrukcje asm w grze mogą to powodować? Gra nie korzysta z własnych przerwań.

Aha, gra jest z roku 1983 i chodzi jedynie pod 48k, próba uruchomienia na 128k kończy się resetem
(nie ja tak zrobiłem, gra ładuje się na zmienne sys i na Basic).

matofesi:
Ale to jest prosta sprawa - jeśli poprawnie uruchamiasz muzykę na przerwaniach i w trakcie gry muzyka się zatrzymuje a potem włącza to oznacza, że gra w którymś miejscu po prostu wyłącza przerwania a potem je włącza. I jeśli faktycznie nie używa własnej procedury obsługi przerwań to nie ma innej możliwości - coś musiałeś przegapić. Albo gdzieś jest jakieś DI albo na przykład gra skacze gdzieś do ROMu i tam jest DI.

rafamiga:

--- Cytat: RafalM w 2012.04.09, 12:00:57 ---Problem w tym że przy losowym n procesor mógłby przeczytać adres startując od złego bajtu jako 1,2 (czyli 2*256+1=513) zamiast 2,1 (1*256+2=258).
Dlatego najprościej jest umieścić przerwanie pod takim adresem gdzie starszy bajt=młodszy bajt i wypełnić wektor przerwań, czyli 256 bajtów wskazywanych przez I*256+n jedną wartością. Wtedy pomyłki na pewno nie będzie.

--- Koniec cytatu ---

A prawdziwi harcerze zmiast marnować 257 bajtów pamięci na tablicę z wektorem procedury obsługi przerwania ustawiali I na na $39, w adres $ffff wpisywali $18, a pod $fff2 i dalej -- odpowiednio $c3 i adres procedury obsługi przerwań, dawali IM 2 i zaczynało działać. 8^)

Pierwszej osobie, która dokładnie opiszę OCB [o co biega] i kiedy to nie zadziała, stawiam piwo przy najbliższej okazji. 8^)

Abrimaal:
Ja tak kiedyś robiłem, że adres vektorów przerwań był ustawiony na obszar w ROMie wypełniony 255 (na ZX48), co dawało wywołanie procedury przerwań od adresu 65535, tam się wpisywało JR, a jego parametrem było 243 zawarte w bajcie o numerze 0 (był on traktowany jako bajt nr 65536, którego Z80 nie zaadresuje). W rezultacie dawało to JR -13, i 13 bajtów przed umieszcza się skok do właściwej procedury obsługi przerwań.
Kiedy nie zadziała - na ZX 128, Pentagon i innych które mają cały ROM zajęty kodem.

Nawigacja

[0] Indeks wiadomości

[#] Następna strona

[*] Poprzednia strona

Idź do wersji pełnej