Start | Super Packer | Atari Graphics Studio | Graph2Font | Mads | MadPascal | Atari Zines | YouTube http://madteam.atari8.info  
MEMBERS
PRODUCTIONS
S C E N E
G A M E S
T O O L S
V B X E
GRAPHICS
WORK IN PROGRESS
Bubble Shooter
HARDWARE
Snes2Joy / Pad4Aatari / TOM rev2
Sio2SD / Pajero
Sio2SD / Rocky
Stereo / Pajero
GTIA / Psychol
ANTIC + VBXE test
ARTICLES / MAGAZINES
DEMO EFFECTS
LINKS
   

Jaskier / Taquart
IRQ Loader
Energy #2

Czołem towarzysze. Tak jak obiecywaliśmy w naszym magazynie znajdziecie wiele pożytecznych informacji i programów, które mogą się przydać przy tworzeniu własnych programów użytkowych oraz dem. W tym artykule akurat znajduje się coś pożytecznego dla twórców dem.

Proces ładowania, to w demach newralgiczne miejsce. Chciałoby się ten czas wykorzystać, podobnie jak w demach na C64, na jakieś efekty. Niestety standardowa procedura na to nie pozwala. Zajmuje po prostu cały czas procesora, chociaż przez większość czasu oczekuje w pętli na ustawienie się jakiś znaczników.

O tym, że można inaczej, przekonują nas przykłady takich dem jak Overmind.
Podczas ładowania pokazują się tam efekty, gdyż cała procedura ładowania realizuje się w przerwaniach IRQ. Podobnie jest w przypadku magazynu Energy. Ponieważ zaś nigdy nie robimy tajemnic z naszych rozwiązań (pod warunkiem, że sami wykorzystaliśmy je już dużo wcześniej), więc postanowiliśmy przedstawić tutaj nasz sposób.

Za źródło niech posłuży nam loader użyty w Energy #1 do ładowania intra. Znajduje się on tam w trzech pierwszych sektorach dysku. Wszystkie inne używane przez nas IRQ-loadery są w istocie bardzo podobne do tego. A zatem zaczynamy.

Loader przystosowany jest na sektory po 128 bajtów. Plik zaś zapisany jest w sektorach o strukturze DOS-owej (ostatnie 3 bajty sektora nie stanowią danych pliku). Sam plik zaś również ma strukturę DOS-ową. Ponadto, ponieważ nie używamy systemu, więc absolutnie się nim nie przejmujemy wyłączając ROM i używając dolnej części strony zerowej. Ponieważ dysk na którym ten loader miał być nagrany był całkowicie w formacie DOS-owym, więc musiałem dokonać paru karkołomnych sztuczek, aby loader zmieścił się w tych wyznaczonych 3 sektorach. Zmniejsza to czytelność programu. Trudno.

Autorem IRQ-SIO jest Electron, zaś cołości loadera Jaskier.

* IRQ-SIO Loader

bufor equ $680 ; 128 bajtów na dane odczytane z sektora.

dcb equ 0      ; 4 bajtowy bufora na dane wysyłane do stacji dysków.
               ; Jest to kolejno:
               ; -bajt identyfikujący urządzenie. Każde
               ; urządzenie: D1,D2,D3,D4,P1,P2,R1 itd. ma
               ; własny kod.
               ; -kod operacji. Dokładnie taki sam jak w operacjach SIO.
               ; -numer sektora. Młodszy i starszy bajt.

cksum equ 4    ; używane do liczenia sumy kontrolnej zarówno przy 
               ; wysyłaniu komendy do stacji, jak i przy odbiorze.

xdone equ 5    ; stan loadera:
               ; 0   - odczytywane są dane bloku,
               ; 1-4 - odczytywany jest nagłówek bloku.

lindex equ 6   ; stan przy wysyłaniu komendy do stacji:
               ; 0-3 - wysyłanie tablicy dcb,
               ; 4   - wysyłanie sumy kontrolnej,
               ; 5   - wywołanie przerwania końca transmisji.

stcnt equ 7    ; stan przy odczycie sektora:
               ; $fe - odczytany został pierwszy bajt od
               ; stacji. Jeśli jest to wartość $41, to znaczy,
               ; że stacja prawidłowo odebrała komendę.
               ; $ff - drugi bajt. Jeśli jest to $43, to znaczy,
               ; że sektor na dysku został prawidłowo
               ; odczytany. Przystępujemy do transmisji.
               ; 0-127 - dane sektora.
               ; 128 - bajt sumy kontrolnej.

adr equ 8      ; adres początku bloku.
end equ 10     ; adres końca bloku.

 org $500

loader dta b(0),b(3),a(loader),a(4)

start sei
 cld
 jsr del       ; skok do procedury przygotowującej stację do wysłania komendy
 stx $d40e     ; w X jest 0
 stx $d400
 lda #$fe
 sta $d301

 lda #$52      ; komenda odczytu sektora
 sta dcb+1
 lda #4        ; numer pierwszego sektora
 sta dcb+2     ; odczywywanego pliku
 lda #$28      ; ustawiamy częstotliwość zegara
 sta $d204
 sta $d208
 stx $d206
 stx dcb+3
 inx           ; najpierw będziemy odczytywać
 stx xdone     ; nagłówek bloku

 lda irq
 sta $ffff
 jsr set       ; rozpoczęcie wysyłania komendy
 cli
 bne *         ; a tu każdy może wsadzić nawet
               ; wektorówkę (jeśli potrafi)
               ; P.S. ten rozkaz BNE oznacza tyle co JMP

init jmp ($2e2); plik może mieć inity

del lda #$34   ; informujemy stację, że coś do
 sta $d303     ; niej będziemy wysyłać
 ldx #0        ;  ustawienie licznika wysyłanych
 stx lindex    ; bajtów
 inx           ; czekamy trochę, aż stacja
 bne *-1       ; przetrawi fakt, że coś od
 rts           ; niej chcemy. Niestety nie
               ; można skrócić tej pętli, gdyż niektóre
               ; stacje są bardzo powolne np. Tygrys Turbo

set lda #$23   ; ustawiamy POKEY-a do
 sta $d20f     ; transmisji (zapisu)
 lda #$10      ; przerwanie zapisu
 sta $d20e
 lda #$31      ; wysyłamy pierwszy bajt do
 sta $d20d     ; stacji
 sta cksum     ; zliczamy sumę kontrolną
rts rts

* przerwanie zapisu danych *

irq2 cmp #$20  ; czy to aby na pewno do mnie?
 bne irq3      ; do przerwania koca transmisji
 lsr @         ; ustawiamy na nowo przerwanie
 sta $d20e     ; do zapisu
 inc lindex
 ldx lindex
 cpx #4        ; czy nadal wysyłamy komendę?
 bcs oi1
 lda dcb,x
 sta $d20d     ; wysyłamy komendę
 clc           ; zliczamy sumę
 adc cksum
 adc #0
 sta cksum
 bcc irqend    ; kończymy przerwanie (bcc=jmp)
oi1 bne oi2
 lda cksum     ; wysyłamy sumę kontrolną
 sta $d20d
irqend pla     ; kończymy przerwanie
 tax
 pla
 rti
oi2 lsr @      ; ustawiamy przerwanie koca
 sta $d20e     ; transmisji
 bne irqend    ; (bne=jmp)

* przerwanie końca transmisji *

irq3 lda #$13  ; ustawiamy POKEY-a do
 sta $d20f     ; transmisji (odczytu)
 sta $d20a     ; to nie jest ustawianie generatora liczb losowych :-)
               ; W trybie zapisu ten rejestr pełni funkcję
               ; resetu złącza szeregowego. Jeśli nastąpił
               ; błąd transmisji to musimy złącze resetować,
               ; a skoro tak, to dlaczego nie robić tego
               ; po prostu zawsze? P.S. wsadzana wartość
               ; nie mają znaczenia (podobnie jak przy $d01e).

 lda #$20      ; przerwanie odczytu
 sta $d20e
 lda #$3c      ; informujemy stację, że coś
 sta $d303     ; będziemy od niej odbierać
 lda #$fe      ; zerowanie licznika
 sta stcnt     ; odbieranych bajtów
 lda #0        ; i sumy kontrolnej
 sta cksum
 beq irqend    ; (beq=jmp)

* główne przerwanie *

irq pha
 txa
 pha
 ldx #0
 lda $d20e     ; sprawdzamy jakie nastąpiło przerwanie (bity przy odbiorze są
               ; negacją tych wsadzanych do tej komórki)
 stx $d20e     ; zerowanie rejestru, aby mógł wskazywać kolejne przerwania
 and #$30
 cmp #$10
 bne irq2

* przerwanie odczytu *

 asl @         ; ustawiamy na nowo przerwanie
 sta $d20e
 lda $d20f     ; pobieramy status błędu
 sta $d20a     ; kasujemy status błędu
 bpl error     ; skasowany bit najwyższy i 6-ty
 and #$20      ; informują, że nastąpił błąd
 beq error
 ldx stcnt     ; czy pobieramy teraz dane
 bmi getsum    ; sektora? (X=0-127)
 lda $d20d     ; pobieramy bajt
 sta bufor,x   ; i do bufora
 clc           ; zliczmy sumę kontrolną
 adc cksum
 adc #0
 sta cksum
 inc stcnt     ; powiększamy ilość
 pla           ; odczytanych bajtów
 tax
 pla
 rti
getsum cpx #$80; czy przesyłana jest
 beq ii1       ; suma kontrolna?
 inc stcnt     ; jak nie, to powiększ ilość
 lda $d20d     ; przeczytanych bajtów i
 cmp #$41      ; sprawdź, czy bajt statusu
 beq endirq    ; wysłanego przez stację
 cmp #$43      ; wskazuje, że wszystko O.K.
 beq endirq
error jsr del  ; jeśli nastąpił błąd, to
 jsr set       ; odczytaj sektor jeszcze raz
endirq pla     ; koniec przerwania
 tax
 pla
 rti
ii1 lda $d20d  ; sprawdzamy sumę kontrolną
 sbc cksum     ; jeśli wszystko O.K., to dane
 bne error     ; z bufora przepisujemy

* loader właściwy *

 tax zerujemy  ; licznik odebranych bajtów
 stx lindex
 lda bufor+$7e ; który następny
 sta dcb+2     ; sektor odczytać?
 lda bufor+$7d
 and #3
 sta dcb+3
 ora dcb+2     ; jeśli zerowy, to znak, że to
 beq l7        ; koniec pliku
 lda #$34      ; jeśli jednak będziemy jeszcze
 sta $d303     ; odczytywać dane, to
               ; przygotowujemy stację na zapis komendy
               ; (czas trwania przepisywania danych będzie
               ; tą pętlą służyć wyczekaniu, aż stacja
               ; załapie o co chodzi).
l7 sta l8+1    ; l8+1 to bajt, który mówi nam
               ; czy będziemy jeszcze odczytywać sektory

 tya           ; zachowujemy dodatkowo Y
 pha
l1 lda bufor,x
 ldy xdone     ; jaki jest stan loadera
 beq l2        ; 0 = odczytujemy dane
 sta adr-1,y   ; nie zero? to znak, że jest
               ; to adres bloku
 lda rts      ; adres nie był tam ustawiony
 sta $2e3      ; to procesor napotka RTS
 inc xdone     ; zwiększ stan loadera
 cpy #2        ; czy przesłany został cały
 bne l4        ; adres początku bloku?
 lda adr       ; sprawdź, czy nie wynosi on
 and adr+1     ; $ffff, jeśli tak, to znaczy, że
 eor #$ff      ; jest to tylko informacja, że
 bne l3        ; ten plik jest binarny
 lda #1        ; przeczytaj adres jeszcze raz
 bne l6        ; (bne=jmp)
l4 tya
 sec
 sbc #4        ; sprawdź, czy przeczytany już
 bne l3        ; został cały nagłówek
l6 sta xdone   ; jeśli tak, to odczytujemy
 jmp l3        ; dane bloku

l2 sta (adr),y ; umieszczamy bajt
 lda adr       ; sprawdzamy, czy to już
 cmp end       ; cały blok
 lda adr+1
 sbc end+1
 bcc l5
 inc xdone     ; jeśli tak, to przystępujemy
               ; na nowo do czytania nagłówka
 stx cksum X   ; może nam się przydać
 jsr init      ; po przeczytaniu całego bloku
               ; robimy skok po $2e2. Standardowo ustawiany jest tam
               ; skok pod rozkaz RTS.

 ldx cksum     ; bierzemy X z powrotem
l5 inc adr     ; następny bajt wsadzimy w
 bne l3        ; następną komórkę
 inc adr+1
l3 inx
 cpx bufor+$7f ; sprawdzamy, czy tylko tyle bajtów w tym sektorze należy do pliku
 bcc l1        ; jeśli nie, to odczytujemy następny
 pla           ; pobieramy Y z powrotem
 tay
l8 lda #10     ; jeśli to był ostatni sektor pliku, to tutaj jest #0
 beq *+5       ; a wówczas...
 jmp error+3   ; odczyt następnego sektora
 jmp ($2e0)    ; uruchamiamy program

 end

No i tak to wygląda. Może na początku jest to nieco skomplikowane, ale po bliższym przyjrzeniu się, każdy powienien wszystko zrozumieć. Aby było łatwiej, wyjaśnię może dokładniej procedurę odczytu i zapisu:

Proces zapisu bajtu do stacji polega na tym, że bajt wysyłany wsadzamy do komórki $d20d, a przerwanie zapisu następuje PO wysłaniu tego bajtu do stacji.
Tak więc pierwszy bajt wsadzamy poza przerwaniem, drugi w przerwaniu informującym, że pierwszy bajt został wysłany itd. W przerwaniu informującym, że wysłana została suma kontrolna (która zawsze jest wysyłana na końcu wysyłanego bloku danych) ustawiamy jako następne przerwanie zakończenia transmisji (w domyśle zapisu, gdyż tylko wtedy to przerwanie jest wykorzystywane).

Proces odczytu jest trochę prostszy. Przerwanie odczytu wywoływane jest tutaj zawsze po odczytaniu bajtu ze stacji. W przerwaniu właśnie ten bajt odczytujemy z komórki $d20d. Po odczytaniu wszystkich bajtów odczytujemy sumę kontrolną. Należy przy tym uważać gdyż bajty statusu stacji, wysyłane przez nią dla potwierdzenia, że dostała i zrozumiała komendę, nie są do sumy kontrolnej wliczane.

A teraz po kolei w punktach proces odczytu sektora:

1) Wysłanie 4 bajtów komendy + suma.

2) Natychmiast po tym, stacja wysyła nam bajt $41, który informuje nas, że stacja zrozumiała komendę.

3) Teraz następuje chwila przerwy. Stacja musi zakręcić dyskiem i odczytać sektor.

4) Jeśli sektor na dysku był prawidłowy, stacja wysyła nam bajt $43 który informuje nas, że wszystko O.K. i zaraz nam wyśle dane.

5) Odbieramy 128+1 (256+1 lub 512+1 -zależnie gęstości dyskietki) bajtów danych sektora z sumą kontrolną.

6) Koniec transmisji sektora.

A teraz dla aktywnych zapis:

1) 4+1 bajtów -komenda + suma.

2) Natychmiast po tym stacja wysyła nam $41, że zrozumiała komendę.

3) Wysyłamy 128+1 (256+1 lub 512+1 -zależnie od gęstości dyskietki) bajtów danych sektora z sumą kontrolną. Ponieważ stacja jest przygotowana na to, nie musimy dawać takiej długiej pętli po umieszczeniu #$34 w komórce $d303.

4) Teraz następuje chwila przerwy. Stacja zakręci dyskiem i zapisze sektor.

5) Odbieramy bajt $43, który oznacza, że sektor został prawidłowo odebrany i zapisany.

6) Koniec transmisji.

I co? Nadal jest to takie trudne?

Jak zapewne zauważyliście w Energy #1 jest więcej niż jeden IRQ-loader. Drugi uruchamiany jest po intrze podczas ładowania całego magazynu.
Jego cechą charakterystyczną jest to, że oprócz muzyczki na dwóch kanałach, odgrywane są tam na dwóch kanałach sample. Wbrew pozorom jest to bardzo prymitywny efekt.
Zauważcie bowiem, że do operacji zapisu-odczytu używane są faktycznie generatory 3 i 4 POKEY-a, ale tylko i wyłącznie dla ustalenia częstotliwości transmisji. Ich głośność do tego nic nie wnosi, a tylko tyle potrzeba, aby sample mogły grać.
Dodatkowo mamy jeszcze jedno ułatwienie. Po wsadzeniu wartości $34 do komórki $d303 nie musimy robić takiej długiej pętli oczekując na to, aż stacja załapie, że chcemy do niej coś wysłać. Po prostu w tym czasie wywołujemy player. Drobne problemy nastręczyć może procedura odgrywania sampli. Nie możemy jej przerywać na nazbyt długo, bo sample zaczną źle brzmieć, a procedura przepisująca bufor do pamięci trwa naprawdę sporo.
Rozwiązanie jest proste. Ta procedura nie będzie znajdować się w przerwaniu, ale w normalnym programie, do którego skaczemy po ustawieniu się znacznika, że cały sektor został przeczytany, a w jej środek wsadzamy drugą procedurę odgrywania sampli. Całość działa w sposób następujący.
Przepisanie jednego bajtu z bufora, odegranie sampla, przepisanie bajtu z bufora, odegranie sampla itd.

Trzecim typem loadera użytym w Energy #1 jest loader ładujący artykuły. Wbrew pozorom nie jest on identyczny z loaderem przedstawionym w tym artykule. Ktoś spostrzegawczy zauważył zapewne, że podczas działania tego loadera na ekranie znajduje się logos w pięciu kolorach. A skoro tych kolorów jest pięć, to znaczy, że jest na fontach. A skoro ten logos jest duży, to znaczy, że są 2 generatory znaków. A skoro są 2 generatory, to znaczy, że gdzieś w środku jest przerwanie NMI. A skoro jest przerwanie NMI, to nie może być przerwań IRQ, gdyż mają one tę właściwość, że mogą opóźnić odebranie NMI. A zatem całość musi być poza przerwaniami IRQ.
Niby niemożliwe, ale zauważmy, że tak naprawdę, to przerwania potrzebne są nam tylko do tego, aby dowiedzieć się kiedy odebrać następny bajt ze stacji, albo go do niej wysłać. Tego zaś możemy dowiedzieć się z komórki $d20e. Bity w niej są bowiem kasowane właśnie przy wystąpieniu danego przerwania. Loader jest dość podobny do tego przedstawionego tutaj. Różnice polegają na tym, że nie ma rozkazu CLI, a procedury w przerwaniach są przeniesione do głównego programu i dodana jest procedurka śledząca komórkę $d20e i w zależności od niej skacząca do odpowiednich procedur.

I to by było na tyle, jeśli chodzi o własne procedury obsługi stacji. W następnym numerze postaramy się rozszerzyć ten temat o procedury obsługi w różnych systemach turbo oraz (jeśli uda nam się namówić na to Foxa) procedury dla stacji Karin.

Jaskier/Taquart

 

madteam.atari8.info © MadTeam, hosted: www.atari8.info