ZX Spectrum > PROGRAMOWANIE
Przerwania - ZX Spectrum 48
appblue:
Witajcie,
Przeglądając kod mojego starego demka (z przełomu 1990/1991) znalazłem ciekawą (niestety bardzo słabo przenośną na klony, inne modele i rozszerzone gumiaczki) metodę uruchamiania kodu na przerwaniach.
Metoda polega na wykorzystaniu trybu przerwań IM 2 procesora Z80 (tryb ma chyba nazwę vectored interrupt), a polega na tym, że do rejestru I wstawia się starszy bajt adresu tablicy zawierającej adresy procedur obsługujących przerwania, której adres w teorii (chyba również w praktyce, choć podobno Z80 nie obsługuje poprawnie nieparzystych indeksów, czyli potrafi procedurę z spod nieparzystego indeksu również wywołać) powinna być w momencie wygenerowania przerwania wczytana z szyny danych z urządzenia zewnętrznego.
Trik, który stosowaliśmy polegał na dwóch założeniach:
* W niezmodyfikowanym ROM-ie ZX Spectrum 48k od adresu $3900 znajduje się ciągły blok ponad 257 bajtów wypełnionych wartościami $FF, więc ustawienie rejestru I na wartość $39 (przy trybie przerwań IM 2) powodowało przy każdym przerwaniu VBLANK skok do adresu $FFFF, bo niezależnie jaka wartość była aktualnie na szynie danych ($00 - $FF), to w bloku pamięci ROM wskazanym jako tablica wektorów przerwań ($3900 - $3A00, bo I = $39) zawsze otrzymywaliśmy adres procedury obsługi przerwania = $FFFF.
Uwaga: tablica wektorów przerwań musi mieć rozmiar 257 bajtów, a nie 256, bo potencjalnie na szynie danych może pojawić się wartość $FF i wtedy adres procedury obsługi przerwania jest pobierany z lokalizacji $39FF i $3A00 (257 bajt, licząc od $3900).
* W pamięci ROM pod adresem $0000 mieści się wartość $F3, więc jeśli do komórki pamięci $FFFF wpiszemy opcode dla rozkazu JR ($18), to rozkaz ten przy wykonaniu w trakcie obsługi przerwania pobierze swój operand z kolejnego adresu, czyli $0000, co w efekcie spowoduje skok pod adres $FFF4, gdzie umieszczaliśmy rozkaz JP irq_routine
Przykładowy kod, który korzysta z ten sztuczki i działa chyba tylko na oryginalnym ZX Spectrum 48k (w formacie pasmo) może wyglądać tak:
--- Kod: --- org $8000
start:
call irq_init
; basically do nothing, as the main job of changing backgroud colors
; is done in the irq routine
loop:
halt
jr loop
irq:
di
push af
push hl
ld hl, color
ld a, (hl)
and 7
out (254), a
inc (hl)
pop hl
pop af
ei
ret
; VARIABLES
color: db 0
;; ========================================================
;; IRQ Init Routine
;; ========================================================
irq_init:
halt
di
ld hl, $FFFF
ld (hl), $18 ; JR opcode
; set the IM2 mode to use jump table at $3900 (block of $FF in ROM)
ld a, $39
ld i, a
im 2
; insert JP irq at address $FEFE
ld hl, $FFF4
ld (hl), $C3 ; JP opcode
inc hl
ld (hl), LOW irq
inc hl
ld (hl), HIGH irq
ei
retn
end $8000
--- Koniec kodu ---
Tak to robiliśmy wtedy ... oczywiście ten kod można trochę poprawić (pracuje wtedy np. na 128k), używając spreparowanej tablicy wektorów w pamięci RAM, co realizuje poniższa procedura irq_init, którą można podmienić we wcześniejszym kodzie.
--- Kod: ---;; ========================================================
;; IRQ Init Routine - more generic
;; ========================================================
irq_init:
halt
di
; fill 257 bytes starting from $FD00 with $FE,
; so IRQ in IM 2 mode will always jump to $FEFE
ld hl, $FD00
ld de, $FD01
ld bc, 257
ld (hl), $FE
ldir
; set the IM2 mode to use jump table at $FD00
ld a, $FD
ld i, a
im 2
; insert JP irq at address $FEFE
ld hl, $FEFE
ld (hl), $C3 ; JP opcode
inc hl
ld (hl), LOW irq
inc hl
ld (hl), HIGH irq
ei
ret
--- Koniec kodu ---
Pozdrawiam i ciekaw jestem waszych uwag!
Phonex:
Tak się wtedy robiło. Tą sztuczkę na forum opisywał rafamiga parę lat temu.
Kto ją spopularyzował w "środowisku"? Nie pamiętam, ale oczywiście też stosowałem.
Ten napisał rafamiga https://www.speccy.pl/forum/index.php?topic=4489.msg72050#msg72050
Ten napisałem ja https://www.speccy.pl/forum/index.php?topic=4489.msg72225#msg72225
Zajrzałem na szybko i jedyna różnica, to z przekory (tak! przypomniałem sobie - chciałem się odróżniać 8) ) użyłem tablicę pod $3B00, a nie $3900 ;)
I to działa na ZX128! Bez żadnego dołączonego interfejsu na szynie jest $FF, więc adres bierze spod $39FF (lub $3BFF lub też $3AFF). W tych trzech strategicznych miejscach projektanci ZX128 specjalnie zostawili dwa $FF! Widać, że specjalnie bo w jednej z tych lokalizacji przed nimi jest skok omijający o dwa bajty :D
W czasie dyskusji na ten temat ktoś (chyba trojacek) napisał, że w zasadzie nawet z dołączonymi interfejsami w momencie odczytu wektora nie ma szans, żeby na szynie było cokolwiek innego niż $FF.
Na Pentagonie też działa. Ta jego część jest zgodna ze 128.
Także nie wiem do końca, czemu ten sposób jest odradzany? Może na innyh klonach nie działa...
Albo może chodzi o niezgodność z dodatkowymi ROMami ZX128? Że np. w edytorze Basica nie działało by? A w +3 w czasie operacji dyskowych?
KWF:
Dzięki za podzielenie się i wyjaśnienia. Swoją drogą, kilka tygodni temu z matem pisałem o lokalizacji wektora przerwań na Discorcie. W zasadzie kiedy wywoływane jest przerwanie wszystkie urządzenia powinny być odłączone od szyny danych, w 99% przypadków (w zasadzie zawsze, o ile nie mamy jakiegoś interfejsu siejącego po szynie danych) procesor odczyta $FF. W „wolnym” bloku ROM są możliwe trzy alokacje wektora przerwań ($3900, $3A00 i $3B00) i z tego co pisał mat, to wystarczy 6 bajtów (3 lokacje po 2 bajty), aby obsłużyć przerwania. Ma to znaczenie, w przypadku modyfikowanych ROMow, wiec warto byłoby zostawiać te 6 bajtów z wartością $FF, a resztę wykorzystać zgodnie z własnym uznaniem.
matofesi:
--- Cytat: Phonex w 2021.10.14, 21:48:19 ---Także nie wiem do końca, czemu ten sposób jest odradzany? Może na innyh klonach nie działa...
Albo może chodzi o niezgodność z dodatkowymi ROMami ZX128? Że np. w edytorze Basica nie działało by? A w +3 w czasie operacji dyskowych?
--- Koniec cytatu ---
Tak - chodzi głównie o inne maszyny - jakieś nietypowe klony w których ludzie dokładają kod w "wolnym" miejscu nie zwracając uwagi na FFy czy też urządzenia podmieniające ROM, które również nie zwracają uwagi na to, żeby zachować te 6 bajtów.
Nie pamiętam o co dokładnie chodziło (ponad 30 lat minęło ;)), ale na 100% były jakieś sytuacje/maszyny na których taka obsługa przerwań nie działała.
trojacek:
--- Cytat: matofesi w 2021.10.15, 09:33:56 ---ale na 100% były jakieś sytuacje/maszyny na których taka obsługa przerwań nie działała.
--- Koniec cytatu ---
All-RAM?
Nawigacja
[#] Następna strona
Idź do wersji pełnej