Autor Wątek: Przerwania  (Przeczytany 15813 razy)

Abrimaal

  • *****
  • Wiadomości: 965
  • Miejsce pobytu:
    Lemmingrad
  • Zamulator
    • Games for ULA plus
Przerwania
« dnia: 2012.04.08, 06:02:36 »
Chciałbym, żeby podczas pracy programu chodziła (sama się wywołując) muzyka na przerwaniach.
Tutaj już nie pamiętam co i jak robić.
Załóżmy że główna pętla muzyki znajduje się pod 32768
Robię w dowolnym podzielnym przez 256 miejscu, 256B z adresem 32768 (0, 128 na zmianę)
(nie chcę używać tabeli 255,255 z ROM 48, bo chciałbym żeby to działało na 128 też.)
teraz chyba muszę wpisać wartości do rejestrów IR (tylko jakie)?
Czy do rejestru I adres tabeli?
I IM2 po uprzednim wywołaniu INIT muzyki powinno ją odgrywać niezależnie od chodzącego programu
(o ile nie ma w nim DI lub zmiany trybu przerwań)
Czego tu jeszcze brakuje?
« Ostatnia zmiana: 2012.04.08, 07:04:55 wysłana przez Abrimaal »
AY Music, ULA plus.

YERZMYEY/HOOY-PROGRAM

  • *****
  • Wiadomości: 1187
  • Miejsce pobytu:
    Rubber Planet
  • AY RIDERS
    • ZX Spectrum demos/games
Odp: Przerwania
« Odpowiedź #1 dnia: 2012.04.08, 11:24:11 »
Ja to mam tylko engine w interpreterze BASICa do tego. Więc tobie to pewnie do niczego nie jest potrzebne. ;)

matofesi

  • *****
  • Wiadomości: 2049
  • Miejsce pobytu:
    Toruń/Poland
Odp: Przerwania
« Odpowiedź #2 dnia: 2012.04.08, 12:10:29 »
Czego brakuje... Przede wszystkim - jak zwykle - chęci przeczytania czegokolwiek na ten temat :P

Przerwania Z80 w trybie IM2 obsługiwane są w następujący sposób:
1. zgłoszenie przerwania z zewnątrz
2. procesor pobiera młodszy bajt adresu wektora obsługi przerwań z szyny danych
3. starszy bajt adresu wektora obsługi przerwań pobierany jest z rejestru I
4. spod w ten sposób skonstruowanego adresu pobierane są dwa bajty i następuje skok do właściwej procedury - cała obsługa przerwania od wywołania do wykonania pierwszej instrukcji w procedurze obsługi zajmuje 32 takty (to jakby ktoś potrzebował coś dokładnie policzyć i synchronizować).

Wektoryzowanie obsługi przerwań służy zasadniczo do tego, żeby różne urządzenia zewnętrzne mogły mieć swoje własne procedury obsługi przerwań - urządzenie zgłaszając przerwanie wystawia na szynie danych własny numer wektora (niektóre źródła podają, że bajt ten powinien mieć wyzerowany najniższy bit, inne, że procesor najniższy bit po prostu ignoruje) a dzięki temu procesor skacze do właściwej procedury.

W wypadku Spectrum cały mechanizm w zasadzie nigdy nie był używany - nie były nigdy produkowane urządzenia, które same z siebie zgłaszałyby przerwania. Stąd w zasadzie jedynym źródłem przerwań jest ULA generująca przerwania "zegarowe" ~50Hz. Problem w tym, że cały mechanizm obsługi w procesorze nadal działa i nadal młodszy bajt pobierany jest z szyny danych co powoduje, że w wypadku podłączania różnych urządzeń zewnętrznych może się tam pojawić w zasadzie dowolna wartość. Bez dodatkowych urządzeń zwykle jest to jednak 255. Niezależnie od tego aby zapewnić na 100% poprawną obsługę przerwań należy zastosować taką procedurę:

1. wyłączamy przerwania
2. przygotowujemy tablicę wektorów przerwań - musi się zaczynać od pełnej strony adresowej (zera w ośmiu najmłodszych bitach adresu). i zawierać 257 identycznych wartości. Wartości te składają się na adres procedury obsługi przerwań. Jeśli na przykład tworzymy tablicę wektorów pod adresem $8000 (32768) i wypełniamy ją wartością $81 to właściwa procedura będzie się znajdowała pod adresem $8181 (33153)
3. Pod adresem procedury możemy umieścić własny kod albo skok w inne miejsce pamięci, gdzie znajdzie się właściwa procedura.
4. do rejestru I ładujemy numer strony tablicy wektorów (starszy bajt jej adresu - wg przykładu $80).
5. przełączamy tryb obsługi przerwań - IM 2
6. włączamy przerwania
7. wykonujemy własny kod
8. wyłączamy przerwania
9. przełączamy tryb obsługi przerwań - IM 1
10. włączamy przerwania
20 wychodzimy do BASICa

Procedura obsługi przerwania musi oczywiście zapamiętać na początku wszystkie rejestry, których będziemy w niej używać a następnie przed zakończeniem musi je odtworzyć do stanu sprzed jej wywołania. Obsługę przerwania kończymy włączeniem przerwań (nie trzeba ich na początku wyłączać - robi to automatycznie procesor w cyklu przyjęcia przerwania) i wychodzimy rozkazem RET lub RETI. Ten drugi jest dłuższy, ale za to wystawia dodatkowe sygnały na szynie, co powoduje, że urządzenie zgłaszające przerwanie z zewnątrz jest w stanie rozpoznać fakt zakończenia obsługi tego przerwania i odpowiednio na niego zareagować. W wypadku Spectrum nie ma to strategicznego znaczenia i spokojnie możemy kończyć przerwanie używając RET.

Abrimaal

  • *****
  • Wiadomości: 965
  • Miejsce pobytu:
    Lemmingrad
  • Zamulator
    • Games for ULA plus
Odp: Przerwania
« Odpowiedź #3 dnia: 2012.04.08, 23:57:57 »
Chęć czytania jest, tylko brak źródła. Zrozumiałem wszystko z wyjątkiem pkt.2
2. przygotowujemy tablicę wektorów przerwań - musi się zaczynać od pełnej strony adresowej (zera w ośmiu najmłodszych bitach adresu). i zawierać 257 identycznych wartości.
Czy cała tablica ma się składać z identycznych wartości 257 x #81 w naszym przykładzie?
Czy na początku ma być coś dodane - niezbyt rozumiem to "zera w ośmiu najmłodszych bitach adresu"?
AY Music, ULA plus.

RafalM

  • *****
  • Wiadomości: 1133
  • Miejsce pobytu:
    Sulejówek
Odp: Przerwania
« Odpowiedź #4 dnia: 2012.04.09, 12:00:57 »
Przerwania są dość ciężkie do zrozumienia, ale jak są chęci to się da.

A więc tak:

- adresy komórek pamięci w Spektrum są z zakresu 0-65535 i składają się z dwóch bajtów, starszego i młodszego

 Adres =starszy*256+młodszy.

- gdy ustawiamy przerwanie w trybie IM2 zaczynamy od ustawienia wartości rejestru I.

LD A,x
ID I,A

 Komputer będzie używał adresu I*256+n gdzie niestety n jest w ogólności liczbą losową (wspomniany bajt podawany przez urządzenia zewnętrzne).

Pod adresem I*256+n jest przechowywany ... następny adres :) Tym razem jest to już właściwy adres kodu przerwania.

Załóżmy że chcielibyśmy by kod przerwania był pod adresem 258 czyli 1*256+2. Ponieważ n jest nieznane teoretycznie należałoby wypełnić 256 bajtów powtarzając ten adres:

2,1,2,1,2,1,2....

(młodszy bajt idzie jako pierwszy)

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.

Pyza^Illusion

  • *****
  • Wiadomości: 586
  • Miejsce pobytu:
    Lubań, dolnośląskie
Odp: Przerwania
« Odpowiedź #5 dnia: 2012.04.09, 12:31:01 »
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.

; 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

#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.
ZX Spectrum+ (128kB by STAVI), FDD3000, TI-OF-TTL/ZXVGS, Masterface2b, MacFace II, DivIDE plus, Just Speccy 128...

Abrimaal

  • *****
  • Wiadomości: 965
  • Miejsce pobytu:
    Lemmingrad
  • Zamulator
    • Games for ULA plus
Odp: Przerwania
« Odpowiedź #6 dnia: 2012.04.10, 07:09:26 »
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).
AY Music, ULA plus.

matofesi

  • *****
  • Wiadomości: 2049
  • Miejsce pobytu:
    Toruń/Poland
Odp: Przerwania
« Odpowiedź #7 dnia: 2012.04.10, 09:03:28 »
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

  • ***
  • Wiadomości: 215
  • Miejsce pobytu:
    Warszawa
Odp: Przerwania
« Odpowiedź #8 dnia: 2012.05.12, 22:45:08 »
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.

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^)
« Ostatnia zmiana: 2012.05.12, 23:35:23 wysłana przez rafamiga »
--
rafamiga

Abrimaal

  • *****
  • Wiadomości: 965
  • Miejsce pobytu:
    Lemmingrad
  • Zamulator
    • Games for ULA plus
Odp: Przerwania
« Odpowiedź #9 dnia: 2012.05.12, 23:12:50 »
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.
« Ostatnia zmiana: 2012.05.12, 23:14:31 wysłana przez Abrimaal »
AY Music, ULA plus.

rafamiga

  • ***
  • Wiadomości: 215
  • Miejsce pobytu:
    Warszawa
Odp: Przerwania
« Odpowiedź #10 dnia: 2012.05.12, 23:34:20 »
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.

Nieźle, nie spodziewałem się, że tak szybko przyjdzie odpowiedź, wiszę Ci piwo

W oryginalnym ROM-ie 48k, w obszarze od $386E do $3cff zostały zapisane bajty $ff [vide ftp://ftp.worldofspectrum.org/pub/sinclair/books/CompleteSpectrumROMDisassemblyThe.pdf, strona 221]. Wystarczy na nie ustawić I, a wtedy procesor -- otrzymując żądanie przerwania -- skoczy pod adres $ffff, w którym jest JR [skok relatywny], a -13 bierze się z... pierwszego bajtu ROM-u. Dla Z80 PC=$ffff+1 to po prostu 0, a tam jest instrukcja "DI", w hex $f3, a w kodzie uzupełnień do dwóch -13...
« Ostatnia zmiana: 2012.05.12, 23:43:03 wysłana przez rafamiga »
--
rafamiga

Abrimaal

  • *****
  • Wiadomości: 965
  • Miejsce pobytu:
    Lemmingrad
  • Zamulator
    • Games for ULA plus
Odp: Przerwania
« Odpowiedź #11 dnia: 2012.05.13, 01:37:14 »
Zamiast piwa, mógłbyś pisać liczby w dec, jeśli ktoś nie jest informatykiem z długim stażem, nie wie ile to jest. :D
AY Music, ULA plus.

Pyza^Illusion

  • *****
  • Wiadomości: 586
  • Miejsce pobytu:
    Lubań, dolnośląskie
Odp: Przerwania
« Odpowiedź #12 dnia: 2012.05.13, 09:57:11 »
A prawdziwi harcerze zmiast marnować 257 bajtów... [...] Pierwszej osobie...

Cóż, dla mnie to było tak oczywiste, że nie przyszła mi do głowy ta odpowiedź z ROM'em. Pewnie dlatego, że jak zaczynaliśmy cokolwiek robić, to już się wszyscy uskarżali na to, że brak pełnej tablicy powoduje "wysypki" na różnych sprzętach - wtedy szczególnie Czesi. Nawet przy First Association w regulaminie był punkt wymagający takiego rozwiązania. Coś mi świta, że w The Lyra II ('91) w scrollerze z butelką też było coś napomknięte i skierowane do Agent-X bodajże, że programy nie chcą działać na 128k gdy przerwania są na tablicy z ROM.
Jak się więc okazało, to nie prawdziwi harcerze :p Choć sprytne to było...
ZX Spectrum+ (128kB by STAVI), FDD3000, TI-OF-TTL/ZXVGS, Masterface2b, MacFace II, DivIDE plus, Just Speccy 128...

matofesi

  • *****
  • Wiadomości: 2049
  • Miejsce pobytu:
    Toruń/Poland
Odp: Przerwania
« Odpowiedź #13 dnia: 2012.05.13, 13:32:10 »
Sprytne i w ekstremalnej sytuacji oszczędzało spory kawałek RAMu.

Muszę swoją drogą porobić parę testów na fizycznych maszynach z różnymi konfiguracjami "pociągu" i przetestować czy to, co przeczytałem na temat tego, że Z80 ignoruje najmłodszy bit adresu wektora przerwań i zawsze bierze spod parzystego adresu to prawda - jeśli tak, to oznaczałoby to, że mimo konieczności generowania pełnej tablicy wektorów można oszczędzić na paddingu i procedurę obsługi przerwań umieszczać bezpośrednio za tablicą. Z drugiej strony informację tą znalazłem tylko w jednym źródle (ale podobno potwierdzoną doświadczalnie) i jeśli byłaby prawdziwa to dziwne byłoby ustawianie pełnych tablic przerwań przez wszystkich programistów robiących komercyjne gry itp.

tsulej

  • *
  • Wiadomości: 25
  • Miejsce pobytu:
    Warszawa
Odp: Przerwania
« Odpowiedź #14 dnia: 2012.12.17, 00:39:49 »
Tak dla porządku dodam jedną rzecz związaną z IM1: nie należy używać rejestru IY.
Pewnie o tym wiecie, ale ja potrzebowałem 4 godzin aby do tego dojść...