Download as pdf or txt
Download as pdf or txt
You are on page 1of 11

Doszły mnie słuchy ze poprzednia cześć kursu była dla Was niezbyt zrozumiała, i od kogo musze sie o tym

dowiadywać? Przecież jak


macie jakiekolwiek pytania są od tego komentarze na samym dole tego arta. O, o tam na samym dole.

Przypomnijmy co było do zapamiętania w 4 rozdziale naszej historii o programowaniu 6502. Był nim ANTIC, układ odpowiedzialny za
wyświetlanie grafiki. Macie zapamiętać trzy rejestry: $d400(DMACTL), $d402, $d403(DLPTR) i to wszystko.

DMACTL-$d400, jego cień to bajt 559 w pamięci atarka. Po co nam DMACTL ? Po to, aby `powiedzieć` ANTIC`owi że chcemy obraz o
szerokości normalnej, wąskiej i fullscreen(szeroki), że wogóle nie chcemy obrazu dzięki czemu mamy całą moc procka na nasze dziwne
programiki np. player MOD, itp. Po to nam właśnie rejestr DMACTL. Przykład:

bit 5, DMA dla programu ANTIC-a (1 = włączony)


|
lda #%00100010
sta 559 ^^
bit 1 i 0 określają szerokość ekranu, lub jego brak
-----
0 0 = brak obrazu
0 1 = obraz wąski (192 punkty)
1 0 = obraz normalny (320 punkty)
1 1 = obraz szeroki (384 punkty)

bity 6 i 7 nie są w ogóle wykorzystane przez cokolwiek, są zbędne i w ogóle po co nam :) bity 2, 3, 4 dotyczą spritów i im podobnych,
zostaną opisane w dalszej części tego kursu.

Dlaczego napisałem `sta 559`, a nie `sta $d400`, ponieważ standardowe przerwanie VBL (co 1/50 sekundy) przepisuje wartości z cieni do
odpowiadających im rejestrów. VBL już zatroszczy się aby wartość z 559 wprowadzic do $d400. Przykład:

lda 559
sta $d400

OK, DMACTL mamy już w jednym paluszku, teraz DLPTR. Przez DLPTR rozumiemy rejestr 2-u bajtowy, jak już wyżej wspominałem:
$d402 i $d403, cieniem są komórki 560 i 561. W tych 2-óch bajtach zapiszemy adres programu dla ANTIC-a. ANTIC jest przecież
osobnym układem zajmującym się tworzeniem obrazu na podstawie specjalnego programu, programu którego autorami możemy być my.
Przedtem jednak musimy znać jego składnie, a składnie tą poznaliśmy w części 4-ej kursu. Przykład:

-*-
obraz equ $bc40

lda <program_dla_antica
sta 560
lda >program_dla_antica
sta 561
rts

program_dla_antica dta b($70,$70,$70) ;3x po 8-m pustych linii


dta b($42),a(obraz) ;zawsze musi być przynajmniej jeden rozkaz LMS,
;czyli załaduj licznik
;pamięci obrazu wartością 2-u bajtową, w naszym
;przykładzie będzie to
;a(obraz) i zawsze jest to wartość 2-bajtowa!!

dta b($f,$f,$f,$f,$f,$f,$f,$f) ;tutaj kod konkretnego trybu graficzngo,


;każdy taki bajt odpowiada
;jednej linii obrazu, w naszym przykładzie $f
;czyli tryb 8 Basica

dta b($41),a(program_dla_antica) ;na końcu każdego Anticowego programu musi być


;jeden z dwóch możliwych
;rozkazów: JVB = $41 lub JMP = $01
;w naszym przykładzie jest to $41 i jest to
;w większości używany rozkaz

;powoduje on skok pod adres zapisany w 2-óch


;następnych bajtach czyli
;a(program_dla_antica) wtedy gdy wystąpi pełna
;synchronizacja VBLK czyli
;wiązka elektronów tworzących obraz dotrze do
;prawej dolnej krawędzi kineskopu
-*-

Teraz podam jaki kod ANTIC-a odpowiada trybowi graficznemu Basica(OS), bo tego zabrakło w części 4-ej.

Tryby graficzne:
Kod trybu ANTIC-a Tryb Basica (OS)
$08 $03
$09 $04
$0A $05
$0B $06
$0C $0E
$0D $07 - to ten tryb od Crumbles Crisis :)
$0E $0F - ten musicie znać
$0F $08 - czyli Hi-Res

Z trybem ANTICA $0F związany jest pewien bajer w postaci rejestru GTIACTL ($d01b, cień = 623). Bity 7 i 6 GTIACTL odpowiadają
właśnie za te bajery:

bit 7 bit6
----- -----
0 0 - bez zmian, tryb 8 Basic-a (OS)
0 1 - tryb 9 Basic-a (OS), czyli 16 odcieni szarości
1 0 - tryb 10 Basic-a (OS), czyli 9 różnych kolorów (704-712)
1 1 - tryb 11 Basic-a (OS), czyli 16 kolorów
o stopniu jasności wg. wartości bajtu 712
Przykład:
lda #%01000000
sta 623 ;tryb 9 Basic-a (OS)

Tryby znakowe:

Kod trybu ANTIC-a Tryb Basica (OS)


$02 $00 - czyli ten najbardziej standardowy :)
$03 brak - znak ma wysokość 10 linii
$04 $0C - wykorzystywany w grach np. Misja, Fred
$05 $0D
$06 $01
$07 $02

To była powtórka z ANTIC-a :). Zapoznaliśmy się już z ważniejszymi elementami programowania Atari XE/XL, została nam jeszcze
muzyka, sprity, operacje ze stacją dysków oraz przerwania. Te ostatnie są najważniejsze.

PRZERWANIA

Skoro są najważniejsze to od nich zaczniemy. Mamy przerwania maskowalne NMI i niemaskowalne IRQ. Maskowalne oznacza, że mogą
zostać wyłączne jeśli zajdzie taka potrzeba. IRQ natomiast są tak ważne, że ich zatrzymanie grozi katastrofą.

Tak na prawdę to IRQ mają najwyższy piorytet i jak ruszają wszyscy muszą czekać aż sie skończą. Wszelkie operacje In-Out ze stacją
dysków czy magnetofonem zaprzęgają do działania właśnie IRQ oraz ... Pokey. Swojego czasu w magazynie Energy, panowie z TQA
zamieścili własną obsługę przerwań IRQ, dzięki czemu można podczas odczytu ze stacji odgrywać czasochłonne msx na samplach i
wyświetlać inne fx-y.

Najczęściej wykorzystywać będziemy przerwanie VBL i DLI, ich wektory czyli 2-bajtowe adresy mieszczą się na 2-iej stronie pamięci
(strona to już chyba wiadomo że = $100 bajtom). Jest tak gdy OS jest włączony !
Wektor DLI - ($200) - adres składany jest z zazwartości $200 i $201
Wektor VBL - ($224) - identycznie jak w/w

Taki OS pragnie sobie np. zrealizować przerwanie DLI, bo jakiś użytkownik wstawił rozkaz DLI do programu ANTIC-a (ustawiony bit 7 w
rozkazie, np. dta b($f+$80)) i pozwolił na przerwania NMI wpisując wartość $c0 do NMIEN($d40e), normalnie jest tam wartość $40.
Podsumowując:
-*-
obraz equ $bc40

lda <ant ;młodszy bajt adresu programu ANTIC-a


sta 560 ;do młodszego bajtu cienia DLPTR(560), czyli znajdzie sie to potem w $d402
lda >ant ;starszy bajt adresu programu ANTIC-a
sta 561 ;do starszego bajtu cienia DLPTR(561), czyli znajdzie sie to potem w $d403

lda #$c0 ;wartość $c0


sta $d40e ;do NMIEN
rts

ant dta b($70,$70) ;2 x 8 pustych linii


dta b($70+$80) ;8 pustych linii z przerwaniem DLI, bo `+$80`
dta b($42),a(obraz)
dta b(2,2,2,2,2,2,2,2,2,2,2,2)
dta b($41),a(ant)

-*-

I co ? I nic ! Bo gdzie jest nasze przerwanie DLI ? Tam gdzie wskazuje na nie wektor :) A wektor wskazuje na RTI domyślnie, czyli skończ
przerwanie. Musimy więc sami napisać nową obsługę DLI i zapisać adres tej procedurki obsługi do wektora. Przed modyfikacją
jakiegokolwiek przerwania musimy poczekać na synchronizację VBLK, czyli:

lda 20 lub lda $d40b


cmp 20 bne *-3
beq *-2

Nasza nowa procedurka obsługi DLI może wyglądać tak:

dli1 pha ;A (akumulator) na stos


tya ;rej.Y do A
pha ;A na stos
ldy #100
l0 tya
sta $d40a ;WSYNC znacznik synchronzacji poziomej, CPU czeka aż ANTIC skończy rysować linie obrazu
sta $d01a
dey
bne l0

pla ;zdejm ze stosu do A


tay ;z A do Y
pla ;zdejm ze stosu do A
rti ;skoncz przerwanie, to taki odpowiednik RTS

Dlaczego ładujemy na stos A i Y, ponieważ gdy następuje dowolne przerwanie, aktualnie wykonywany program zostaje wstrzymany a
sterowanie jest przekazywane do procedury obslugujacej przerwanie. Gdy przerwanie sie kończy nasz wstrzymany program jest
kontynuowany, a więc wszystkie rejestry muszą być niezmienione, muszą zawierać te same wartości co przed przerwaniem inaczej
nastąpiłby kogel-model ;). Możemy też tak:

dli2 sta a+1


sty y+1

ldy #100
l0 tya
sta $d40a
sta $d01a
dey
bne l0

a lda #0
y ldy #0
rti

Wynik będzie ten sam co w dli1, inaczej tylko zapamiętujemy A i Y. Zbierając wszystko do kupy, otrzymujemy:
-*-
obraz equ $bc40

opt h+
org $8000

lda <ant ;adres programu ANTICA


sta 560
lda >ant
sta 561

lda 20 ;czekamy na synchro :)


cmp 20
beq *-2

lda <dli ;mlodszy bajt naszego nowego DLI


sta $200
lda >dli ;starszy bajt naszego nowego DLI
sta $201
lda #$c0 ;pozwalamy na przerwania DLI
sta $d40e
rts

dli pha ;A (akumulator) na stos


tya ;rej.Y do A
pha ;A na stos

ldy #100
l0 tya
sta $d40a
sta $d01a
dey
bne l0

pla ;zdejm ze stosu do A


tay ;z A do Y
pla ;zdejm ze stosu do A
rti ;skoncz przerwanie

ant dta b($70,$70) ;2 x 8 pustych linii


dta b($70+$80) ;8 pustych linii z przerwaniem DLI, bo `+$80`
dta b($42),a(obraz) ;tutaj już wszyscy wiedzą co i jak itd
dta b(2,2,2,2,2,2,2,2,2,2,2,2)
dta b($41),a(ant)
-*-

To był przykład własnej obsługi DLI przy włączonym OS. Teraz koniec z tym, jeśli chcecie napisać demo zapomnijcie o OS. Sami sobie
napiszemy wszystko :D

PRZERWANIA BEZ OS
Poprzedni paragraf potraktujcie jako ciekawostkę, osobiście nie pamiętam ile lat temu skorzystałem z OS w celu modyfikacji przerwań. Po
prostu pisząc własną obsługę przerwań masz pełną kontrolę nad kompem i pamięcią. Nie zajmujemy się tu szkoleniem koderów użytków
tylko koderów `wypaśnych dem` :). W progach użytkowych OS się przydaje, przedewszystkim do odczytu zapisu danych ze stacji. Możecie
jednak posłużyć sie DOS-em jak to jest w przypadku DELTA Trackera.

Demo, demo, demo nie obejdzie sie bez własnej procedury obsługi przerwań. System zostaje wyłączony, dzięki czemu zyskujemy pamięć
$c000-$cfff, $d800-$fff0. "OS z wozu, CPU lżej" :)

Powiedzmy sobie prawde o przerwaniach. Gdzieś w pamięci musi być stały adres procedur obsługi przerwań: IRQ, NMI, RESET. Dlaczego
? Czy zastanawialiście się kiedyś gdy włączacie Atari skąd CPU6502 wie gdzie znajdują się programy przerwań. Jest bowiem w pamięci
takie miejsce, które zna 6502, wpisano mu je na stałe do jego krzemowej struktury. Oto one:
($FFFA) - NMIVEC
($FFFC) - RESETVEC
($FFFE) - IRQVEC

My zajmiemy sie tylko NMI bo są fajne, ładne, sprawne i wogóle reszta nam sie nie przyda. Po prostu wyłączymy IRQ i po sprawie.

Z DLI możemy zmienić obraz w konkretnych liniach, np. kolor, pozycja spritów. Nie zmieścimy tyle co na VBL, ale wystarczy aby na
ekranie znalazło się 256 kolorów, zaczął działać TIP itp. Natomiast w trakcie VBL możemy wykonać nawet do 20000 cykli zegara.
Swojego czasu ludziska prześcigali się co też można zmieścic w 1 ramce, czyli w czasie wykonywania VBL-a. !!! Przykład, który jest
poniżej należy traktować jako wzorcowe rozwiązanie, wszyscy mają wykuć to na `blache`. !!!

-*-
opt h+
org $2000 ;adres uruchomienia naszego extra programu, może być wg Waszego widzi-misie

lda 20 ;czekamy ramke zanim zaczniemy szaleć


cmp 20
beq *-2

sei ;wyłączamy nieodwołalnie IRQ


lda #0
sta $d40e ;wyłączamy NMI ($d40e = $00), tak na chwilę, na czas zmiany wektorów przerwań
lda #$fe
sta $d301 ;wyłączamy OS ($d301 = $fe), możemy dzięki temu korzystać z pamięci $c000-$cfff, $d800-$fff0

;i w tym momencie niejako działa już sam CPU, wszystko inne zamarło :)

ldx <nmi ;nasz nowy adres programu NMI


ldy >nmi
stx $fffa
sty $fffa+1

... ;tutaj nasze jakieś inne wartości do incjacji


...
...

lda #$40
sta $d40e ;włączamy NMI ($d40e = $40, tylko VBL), ($d40e = $c0, VBL oraz DLI)

... ;tutaj nasz dalszy program, może jakieś wektorki kto wie :)
...
...

*-------------
* Obsługa NMI
*-------------
nmi bit $d40f ;NMIST - rejestr statusu przerwań NMI
bpl _vbl
dliv jmp dli ;skok do obsługi DLI

_vbl pha ;tutaj nasze VBL, zapamietujemy rejestry A,Y,X na stosie


tya
pha
txa
pha

inc 20 ;licznik zwiekszany co ramke

lda 756 ;adres zestawu znaków


sta $d409
lda 560 ;< adres programu ANTIC-a
sta $d402
lda 561 ;> adres programu ANTIC-a
sta $d403
lda 559 ;DMACTLS
sta $d400
lda 623 ;GTICTLS
sta $d01b

ldx #8
col lda 704,x ;przepisujemy kolory z cieni do ich rejestrow właściwych
sta $d012,x
dex
bpl col

pla ;zdejmujemy ze stosu wartości rejestrów X,Y,A


tax ;zdejmujemy je w odwrotnej kolejności jak je tam kładliśmy
pla
tay
pla
rti

dli rti ;tutaj nasze DLI, pamiętajcie żeby zostawiać rejestry niezmienione
;po zakończeniu przerwania
-*-

Oczywiście możecie zmienić standardowe komórki cieni na swoje własne, jeśli czujecie się na siłach. Może będziecie potrzebowali akurat
strony 2 pamięci. Przykład zastosowania własnej obsługi przerwań i wyłączenia OS. Szybkie tworzenie grafiki (np. rysowanie linii) może
potrzebować aby pamięć obrazu zaczynała się od $0000. Wtedy organizujemy program ANTIC-a w ten sposób, że każda linia obrazu
zaczyna się od nowej strony pamięci. Przykład:

ant dta b($70,$70,$70) ;odpowiednikiem tego jest d`ppp`


dta b($4f),a($0000) ;0 - page
dta b($4f),a($0100) ;1 - page
dta b($4f),a($0200) ;itd
dta b($4f),a($0300)
dta b($4f),a($0400)
dta b($4f),a($0500)
dta b($4f),a($0600)
dta b($4f),a($0700)
dta b($4f),a($0800)
... ;itd
...
dta b($41),a(ant)

Zaledwie 40 bajtów będzie wykorzystane z każdej strony pamięci, resztę możecie przeznaczyc na texture czy inną grafikę. Nie obawiajcie
się, że coś stanie się stosowi (strona 1 = $100), on zapisuje dane od $1ff w dół do $100, a my od $100 do $127 w tym konkretnym
przypadku. Jeśli zorganizujecie inaczej obraz, będziecie musieli wyliczyć adres każdej linii obrazu, żeby móc postawić tam pixel. A w/w
przykładzie nie trzeba:
scr equ $80

jsr init ;przygotowanie tablicy `pozycjaX`


ldx #37 ;pozycja X
ldy #59 ;pozycja Y
jsr plot ;wywołanie podprocedurki PLOT
lop jmp lop ;pętl się w nieskończoność

plot sty scr+1 ;3 cykle


ldy pozycjaX,x ;4 cykle ???
lda (scr),y ;5 cykli
ora kolor,x ;4 cykle
sta (scr),y ;5 cykli
rts ;6 cykli = razem 27 cykli

init ldy #0
in_ tya
lsr @ ;dzielimy przez 8
lsr @ ;
lsr @ ;
sta pozycjaX,y
iny
bne in_
rts

org $a400
kolor :32 dta b(%00000001,%00000010,%00000100,%00001000,%00010000,%00100000,%01000000,%10000000)
pozycjaX org *+256

Tablice `pozycjaX` oraz `kolor` mają wielkość 256bajtów i muszą znajdować sie na początku strony, tzn. młodszy bajt = $00. Całość
zobaczycie w załączniku z przykładami (PLOT.ASM), tak jak i inne źródłowki pokazane tutaj. Ten programik potrafi w 1 ramce wyświetlić
do 606 pixli dla wąskiego obrazu i 596 dla normalnego. Inne rekordy typu 1024pixli itp polegają już na kopiowaniu. Nasz program można
też przyspieszyć, umieszczając główną pętlę i zmienne na stronie $00. Da nam to 631 pixli w ramce (PLOT2.ASM) dla wąskiego ekranu a
621 dla normalnego. 1 cykl zaoszczędziliśmy m.in. w ten sposób (PLOT2.ASM):

org GDZIES_NA_0_PAGE

ldx #47 ;pozycja X


lda #55 ;pozycja Y
sta scr+2

plot ldy pozycjaX,x ;4 cykle ???


scr lda $ff00,y ;4 cykli - tu jest nasz 1-cyklowy zysk
ora kolor,x ;4 cykle
sta (scr+1),y ;5 cykli
POKEY

A teraz Pokey (Potentiometr and Keyboard chip) i jego przewyższające wszystkie 8-bitowe układy, możliwosci dźwiękowe. Jeśli Pokey nie
gra tak jak Sid, to tylko dlatego że nie napisano jeszcze odpowiedniego programu ;).

Co nam daje Pokey? Daje nam 4 niezależne generatory dźwięku, sterowane przez dziewięc rejestrów (tylko do zapisu). Oprócz tego
generatory służą jako sprzętowe liczniki i mogą wywoływać przerwania IRQ.

Ponieważ Pokey wykorzystywany jest do obsługi klawiatury i łącza szeregowego przed użyciem jego dźwiękowych możliwości należy go
zaincjować. Jeślibyśmy go nie zaincjowali zawierał by jakieś śmieci z transmisji in-out. Jak incjujemy ? Wpisujemy do rejstru AUDCTL
($d208 - Audio Control) wartość 0, natomiast do SKCTL ($d20f) wartość 3.

lda #0
sta $d208
lda #3
sta $d20f

Teraz możemy grać :). Do tego celu służą po dwa rejestry na kanał. Pierwszy AUDF to rejestr częstotliwości. Drugi AUDC odpowiada za
głośność i zniekształcenia. Oczywiście dla kanału nr 1 mamy odpowiednio AUDF1, AUDC1, dla kanału nr 2 mamy AUDF2, AUDC2 itd.
Jak to działa, że wogóle działa. Rejestr częstotliwości AUDF jest wykorzystywany jako dzielnik impulsów. POKEY zlicza impulsy sygnału
wejściowego doprowadzonego do generatora i przesyła impuls do wyjścia, gdy stan licznika jest zgodny z zawartością rejestru AUDF
zwiększoną o jeden. Przy wartości 0 w AUDF przepuszczany jest każdy impuls wejściowy, a przy wartości np. 3 - co czwarty. Dzielnik ma
więc zakres od 1 do 256. Ponieważ sygnał wyjściowy składa się z pojedyńczych impulsów, to na końcu jest dzielony jeszcze przez 2 przy
pomocy przerzutnika. Otrzymuje się wtedy sygnał o wypełnieniu 1:1 (stosunek czasu trwania impulsu do czasu trwania przerwy między
impulsami). Ta ostatnia operacja jest konieczna dla uzyskania właściwej barwy dźwięku.

Nieco bardziej skomplikowane jest działanie rejestrów sterujących generatorami - AUDC. Poszczególne bity tych rejestrów mają różne
funkcje. Bity 0-3 określają natężenie dźwięku. Skala siły głosu jest więc 16stopniowa, 0-15. Bit 4 wyłącza dzielnik częstotliwości.
Powoduje to pojawienie się na wyjściu stałego napięcia o wartości określonej przez bity 0-3, dzięki czemu mamy zajebistej jakości 4bitowe
sample :). Można wtedy stworzyć dowolny przebieg fali dźwiękowej. Normalny dźwięk wytwarzany przez komputer ma przebieg
prostokątny, jeśli jednak zaczniemy wpisywać do AUDC przy włączonym 4-tym bicie wartości od 0-15 i od 15-0 uzyskamy przebieg
trojkątny, taki jaki tworzą instrumenty dęte blaszane.

lda value0..15 ;jakaś wartość z zakresu 0..15


ora #%00010000 ;ustaw bit 4
sta audc1..4 ;zapisz do jednego z AUDC, od 1 do 4

lub
lda #%00010101 ;bezpośrednio mamy już przygotowaną wartość z ustawionym bitem 4,
;jak w Inertii lub ProTracker-ze
sta audc1..4

Pozostałe bity AUDC kontrolują zniekształcenia dźwięku. Układ POKEY zawiera jeszcze rejestry przesuwające pracujące z częstotliwością
zegara systemu (1.79MHz w systemie NTSC i 2.217MHz w systemie PAL - "ciekawe, no ale skoro pisze tak Wojtek Zientara, to trzeba się
z nim zgodzić :)") W/w rejestry przesuwające mają długość 4, 5 i 17 bitów, przy czym rejestr 17bitowy można przełączyć na 9bitowy.
Impulsy z generatora, po przejściu przez dzielnik częstotliwości, lecz przed podziałem przez 2, są doprowadzane na wejście wybranego
rejestru. Pojawiają się na wyjściu z rejestru w tej samej kolejności (rejestr przesuwający działa jak kolejka w sklepie). Nie byłoby w tym nic
nadzwyczajnego, gdyby nie dodatkowa operacja. Bity pobierane są nie tylko z wyjścia, lecz także ze środka rejestru przesuwającego. Bity
te poddawane są operacji logicznej NOR i ponownie przesyłane do wyjścia rejestru. Powoduje to uzyskanie zakłóceń pseudolosowych,
których powtarzalność zależy od długości rejestru.

Po tym wyjaśnieniu będzie łatwiej zrozumieć działanie trzech najstarszych bitów (5-7) rejestru AUDC. Służą one do wyboru rejestru
przesuwającego, który będzie użyty do zniekształcenia dźwięku. Kolejne kombinacje tych bitów mają następujące znaczenie:

bity 000 - rejestr 5bitowy i 17bitowy


bity 001 - rejestr 5bitowy
bity 010 - rejestr 5bitowy i 4bitowy
bity 011 - rejestr 5bitowy
bity 100 - rejestr 17bitowy
bity 101 - bez rejestru przesuwającego
bity 110 - rejestr 4bitowy
bity 111 - bez rejestru przesuwającego

Pomimo użycia 3bitów kombinacji jest tylko 6, gdyż pary 001 i 011 oraz 101 i 111 są identyczne. Ostatni rejestr POKEY-a, który opiszemy
to AUDCTL ($d208) - steruje on jednocześnie wszystkimi generatorami. Jego użycie znacznie zwiększa możliwości dźwiękowe Atari.
Poszczególne bity tego rejestru mają następujące znaczenie:
Bit 0 służy do wyboru zegara bazowego, czyli źródła impulsów dla wszystkich generatorów. Normalnie zegar bazowy ma
częstotliwość 64khz, a po ustawieniu bitu 0 jest przełączany na 15khz. Jeśli pozostałe parametry pozostaną
niezmienione, to spowoduje to czterokrotne zmniejszenie częstotliwości wszystkich dźwięków.
Bit 1 jego ustawienie włącza filtr górnoprzepustowy w generatorze 2. Filtr ten jest sterowany generatorem 4
i powoduje,że generator 2 emituje tylko dźwięki o częstotliwościach wyższych od częstotliwości generatora 4.
Bit 2 włącza analogiczny filtr górnoprzepustowy dla generatora 1 sterowany generatorem 3.
Bit 3 gdy jest ustawiony, łączy dzielniki generatorów 3 i 4 w jeden dzielnik 16bitowy, który umożliwia bardziej
precyzyjną regulację częstotliwości. Poszerza to również zakres uzyskiwanych częstotliwości ze 125Hz-32kHz
do 0.5Hz-32kHz, a po zmianie zegara bazowego przy pomocu bitu 5 na 16Hz-1MHz.
Bit 4 łączy dzielniki 1 i 2 w jednen dzielnik 16bitowy analogocznie jak w/w bit3.
Bit 5 gdy jest ustawiony przełącza zegar bazowy dla generatora 3 na częstotliwość 2.217MHz (wszystkie źródła
podają tuczęstotliwość 1.79MHz - jest to częstotliwość kwarcowego zegara systemu, ale w NTSC! - komputery
w wersji PAL mają zegar 2.217MHz). Przełączenie to powoduje zwiększenie częstotliwości ponad 30 razy.
Tak wysokiedźwięki nie nadają się do słuchania - wykorzystuje się tą możliwość prawie zawsze jednocześnie
z ustawieniem bitu3.
Bit 6 przelącza zegar bazowy dla generatora 1 na częstotliwość 2.217MHz, podobnie jak w/w bit5
(wszystkie podane tam uwagi odnoszą się także do tego bitu).
Bit 7 przełącza 17bitowy rejestr przesuwający na rejestr 9bitowy. Pozwala to na uzyskanie dwóch dodatkowych
sposobówzniekształcenia dźwięku (dla bitów 5-7 w AUDC równych 000 lub 100).

Ubocznym efektem działania generatorów dźwięku jest liczba pseudolosowa znajdująca się w rejestrze RANDOM ($d20a). Umieszczane
jest tam osiem najstarszych bitów z 17bitowego rejestru przesuwającego.

Przykładem wykorzystania możliwości POKEY-a są wszelkie trackery, jak pierwszy Chaos Music Composer (CMC), później DMC, który
grał 2x na ramke, i najpopularniejszy Music Pro Tracker (MPT). Wywoływanie playera więcej niż 1raz na ramke powoduje że uzyskujemy
inne brzmienia, inną barwę dźwięku. Theta Music Composer (TMC) właśnie oferuje taką możliwość wywołania playera więcej niż 1raz.
Pozostałych programów jest tak wiele że ich nie wymienie, bo te w/w najczęsciej wykorzystuje demo-scena :).

Delta COMPILER.ASM kompilujemy najpierw, potem DLTPLAY.ASM i możemy uruchomić DLTPLAY.OBX (wszystkie pliki w
załączniku).

SPRITY

Sprity czyli po naszemu duszki to sprzętowo sterowana grafika przez układ GTIA i ANTIC, którą możemy nakładać na inne sprity, na
grafikę oraz tło. Jeśli braknie nam spritów sprzętowych, a jest ich tylko 4 lub 5 sztuk musimy posiłkować się spritami programowymi. A że
Atari to wypaśnie szybki sprzęcior nie ma z takimi spritami problemów ;).

Jak już wspomniałem mamy 4 lub 5 sprity, 4 rzeczywiście od razu, a ten 5-y powstaje ze złożenia 4 pocisków. Pociski są 4-y razy węższe
od sprita, czyli jaką szerokość ma sprite? Sprite ma szerokość 8pixli grafiki trybu 15Basica. Pocisk zaś szerokość 2pixli. Wystarczy nam to
na postać bohatera, postać przeciwnika i ... zabrakło ;)

Taki sprite ma 2 kolory, czyli opiszemy go za pomocą 0 i 1. Tam gdzie jest 0 mamy dziure, tam gdzie 1 nasz kolor. Jeśli ma dziure to przez
tą dziure zobaczymy kolor tła, ponieważ sprite jest nad tłem. Przeważnie jest nad tłem, bo za tłem nie byłoby go widać :). Ale wszyscy chcą
więcej kolorów, więc GTIAa da nam więcej kolorów, jeśli tylko ustawimy X bit w DMACTL. Po takiej operacji gdy nałożymy na siebie 2
sprity, tzn oba sprity maja tą samą pozycje poziomą i pionową uzyskamy 4 kolorowe sprity (to tak jak kładziesz sie na dziewczynie, razem
macie więcej kolorów :>).

Wiemy już co oferują nam sprity, podsumowując, (5spritow 2kolorowych) lub (4sprity 2kolorowe i 4pociski 2kolorowe) lub (2sprity
4kolorowe i 4 pociski). Z tym 5-tym jest zawsze więcej roboty. Szerokość to 8pixli dla sprita i 2pixle dla pocisku. Szerokość może być:
pojedyńcza, podwójna lub potrójna (realizowana sprzętowo). Wysokość 256 linii w spritach jednoliniowych i 128 linii w spritach
dwuliniowych. Za jedno i dwu liniowość odpowiada rejestr sprzętowy. Dostępem ANTIC-a do do pamięci ze spritami regulują bity 3-5
rejestru DMACTL:

Bit 3 kontroluje dostęp ANTIC-a do obszaru pamięci dla pocisków. Gdy jest ustawiony,
to dane te są przepisywane z pamięci do rejestru GRAFM (Graphics For Missile).
Bit 4 kontroluje dostęp ANTIC-a do obszaru pamięci dla graczy. Gdy jest ustawiony, to dane te są przepisywane
z pamięci do rejestrów GRAFP0-3 (Graphics For Player 0-3).
Bit 5 ustala rozdzielczość grafiki P/MG. Gdy jest skasowany, to dane P/MG są pobierane do pamięci podczas tworzenia
co drugiej linii ekranu. Każdy pixel obiektu ma więc wysokość dwóch linii ekranu. Po ustawieniu bitu 5 dane są
odczytywane przy tworzeniu każdej linii, więc pixel ma wysokość jednej linii ekranu.
To jest właśnie rozdzielczość dwuliniowa lub jednoliniowa.

Bazowy adres pamięci graczy i pocisków jest umieszczany w rejestrze PMBASE (Player/Missile BASE - $d407). PMBASE musi
koniecznie wskazywać początek bloku 1kb przy rozdzielczości dwuliniowej i 2kb przy rozdzielczości jednoliniowej. Dane poszczególnych
obiektów muszą być umieszczone w pamięci następująco:

rozdz. dwuliniowa
pociski PMBASE+$0180 - PMBASE+$01ff
gracz 0 PMBASE+$0200 - PMBASE+$027f
gracz 1 PMBASE+$0280 - PMBASE+$02ff
gracz 2 PMBASE+$0300 - PMBASE+$037f
gracz 3 PMBASE+$0380 - PMBASE+$03ff

rozdz. jednoliniowa
pociski PMBASE+$0300 - PMBASE+$03ff
gracz 0 PMBASE+$0400 - PMBASE+$04ff
gracz 1 PMBASE+$0500 - PMBASE+$05ff
gracz 2 PMBASE+$0600 - PMBASE+$06ff
gracz 3 PMBASE+$0700 - PMBASE+$07ff

Obiekty mogą być też tworzone na ekranie bez pośrednictwa ANTIC-a i bez korzystania z opisanego wyżej obszaru pamięci. Trzeba w tym
celu wpisać bajt wzoru bezpośrednio do rejestru grafiki GRAFM (pociski) lub GRAFP (gracze). Każdy rejestr GRAFP odpowiada bajtowi
danych gracza, zaś w rejestrze GRAFM każdemu pociskowi odpowiadają dwa bity. Umieszczone w ten sposób dane będą wyświetlane na
całej wysokości ekranu, otrzymamy więc pionowy pas. Zmienić zawartośc rejestrów grafiki tak aby uzyskać widoczny efekt na ekranie
możemy tylko podczas tworzenia obrazu, czyli przy pomocy przerwania DLI.

Niezależnie od sposobu zapisywania danych w rejestrach grafiki (bezpośrednio lub przez ANTIC) należy jeszcze zasygnalizować układowi
GTIA, że dane z tych rejestrów mają być przeniesione na ekran. Do tego celu służą dwa najmłodsze bity rejestru PMCTL (Player/Missile
Control - $d01d). Bit 0 steruje pociskami, a bit 1 graczami. Ustawienia tych bitów powoduje dodawanie danych gracza lub pocisku do
danych tworzonej linii.

Szerokość graczy to 8bitów, pociskow 2bity. Każdy bit reprezentuje 1 pixel obiektu. Pixel ten jest niezależny od aktualnego trybu obrazu.
Wysokość obiektów PM/G jest wyznaczana przez wybraną rozdzielczość i jest jednakowa dla nich wszystkich. Szerokość jest ustalana
przez zawartość rejestru SIZEM lub SIZEP. W rejestrach SIZEP0-3 (Size Player 0-3, $d008-$d00b) szerokość określają dwa najmłodsze
bity (pozostałe są niewykorzystane). W rejestrze SIZEM (Size Missiles, $d00c) każda para bitów określa szerokość pixeli odpowiedniego
pocisku, czyli w jednym bajcie zapisane są szerokości 4pocisków:

bity 0 i 1 - pocisk 0
bity 2 i 3 - pocisk 1
bity 4 i 5 - pocisk 2
bity 6 i 7 - pocisk 3

Szerokość pixeli jest wyznaczana wg schematu:


00 - pixel o szerokości 1 cyklu koloru
01 - pixel o szerokości 2 cyklu koloru
10 - pixel o szerokości 1 cyklu koloru
11 - pixel o szerokości 4 cyklu koloru

Kolory obiektów określają rejestry COLPM0-3 (Color of Player/Missile 0-3, $d012-$d015). Kolory te są identyczne parami, tzn gracz 0 i
pocisk 0 mają kolor z rejestru COLPM0, gracz 1 i pocisk 1 z rejestru COLPM1. Gdy obiekt znajduje sie na tle obrazu, nie ma żadnych
kłopotów. Problem pojawia sie dopiero wtedy, gdy obraz zawiera jakąś treść. Chodzi o to, czy obiekt będzie zasłaniał zawartość obrazu, czy
odwrotnie. Nazywamy to piorytetem - kolor o wyższym piorytecie będzie zasłaniał kolory o niższym piorytecie. Do określenia kolejności
kolorów służy rejestr GTIACTL (GTIA Control - $d01b). Jego bity 0-3 ustalają następujące piorytety kolorów:

bit0 bit1 bit2 bit3


COLPM0 COLPM0 COLPF0 COLPF0
COLPM1 COLPM1 COLPF1 COLPF1
COLPM2 COLPF0 COLPF2 COLPM0
COLPM3 COLPF1 COLPF3 COLPM1
COLPF0 COLPF2 COLPM0 COLPM2
COLPF1 COLPF3 COLPM1 COLPM3
COLPF2 COLPM2 COLPM2 COLPF2
COLPF3 COLPM3 COLPM3 COLPF3
COLBAK COLBAK COLBAK COLBAK

Ustawienie jednego z tych bitów ustala określone przez niego piorytety. Ustawienie więcej niż jednego bitu powoduje niejednoznaczne
określenie piorytetów. W takim przypadku, gdy obszary o jednakowym piorytecie będą się pokrywały, to uzyskają one kolor czarny. Przy
ustalaniu piorytetów trzeba pamiętać, że w niektórych trybach ANTIC-a (2, 3, F) z rejestru COLPF1 jest pobierana tylko jasność pixela. W
tych trybach wszystkie pixele obrazu mają taki jak COLPF2, niezależnie od aktualnego piorytetu COLPF1.

Przy tworzeniu P/MG wykorzystywane są jeszcze 2bity rejestru GTIACTL (4 i 5). Ustawienie bitu 4 powoduje nadanie wszystkim
pociskom koloru określonego rejestrm COLPF3. Można w ten sposób uzyskać 5-ego gracza. Każda część takiego gracza jest jednak
niezależna - może być poruszana oddzielnie i mieć różną szerokość.

Bit 5 wywołuje po ustawieniu zmianę piorytetów par graczy 0-1 i 2-3. Każdy gracz z takiej pary ma kolorów określony odpowiednim
rejestrem. Gdy pixele graczy pokrywają się, to kolor wynikowy jest otrzymywany poprzez operację OR wykonaną na bitach kolorów (np.
dla graczy 0 i 1 - COLPM0 ORA COLPM1). Także w tym przypadku pozostałe parametry gracze są ustalane odrębnie.

Za pozycje poziomą graczy odpowiedzialne są rejestry HPOSP0-3 (Horizontal Position of Player 0-3, $d000-$d003), pocisków HPOSM0-3
(Horizontal Position of Missile 0-3, $d004-$d007). Każdy z nich zawiera numer cyklu koloru, w którym rozpoczyna się przenoszenie na
ekran danych odpowiedniego obiektu.

Przykładem na sprity sprzętowe i ich większą ilośc są gry: Technus, Smuś, Włóczykij. Jak to możliwe ? Możliwe jeśli w jednej poziomym
obszarze będą znajdowały sie max 2-a 4kolorowe sprity, np. powiedzmy 32 linii ekranu. W następnych liniach dzieki przerwaniu DLI
umieścimy następne 2 przeszkadzajki (na spritach przeważnie są nasi wrogowie), itd. Albo tak jak w w/w grach 2sprity na gracza i 2sprity
na przeszkadzajki co 32 linie ekranu.

Duszki programowe najlepiej zobaczyć w Mario Bros, rewelacyjnie zrobionej, dynamicznej i wciągającej gierce, nie to co te pomyje z C-64
;P. W Blinky Scary School nasz bohater jest z duszków sprzętowych, a przeszkadzajki z programowych. Pozatym wszystkie gry w Hi-res
muszą mieć sprity programowe, C-64 posiada duchy w Hi-res, my mamy to programowo, bo stać nas na to :). Teraz mały przykład prostego
wyświetlenia duszka na ekranie, czyli które rejestry wypełniamy:

pm equ $a000 ;od adresu `PM` bedzie nasz obszar na duchy i pociski
py equ 56 ;pozycja Y naszego duszka (duszek nie porusza się w pionie)

opt h+
org $8000

ldx #0
petla lda shape,x ;przepisujemy 25 bajtow z tablicy `SHAPE` pod adres `pm+$400+py`
sta pm+$400+py,x ;w tej tablicy znajduja sie dane opisujace ksztalt duszka
inx
cpx #$19
bne petla

lda >pm
sta $d407 ;PMBASE - starszy adres obszaru z duchami
ldy #2
sty $d01d ;PMCTL - czy wyswietlic duchy lub pociski, czy oba razem
dey
sty 623 ;GTICTLS - piorytet kolorów
dey
sty $d008 ;SIZEP0 - szerokość
lda #$e
sta 704 ;cien rejestru koloru gracza0
lda #%00111010
sta 559 ;DMACTLS - dostep do pamieci dla duchow i pociskow wg odpowiednich bitow

animka lda 20 ;pozycja pozioma bedzie wartosc z zegara systemowego


sta $d000 ;HPOSP0 - pozycja pozioma duszka nr 0
jmp animka

shape dta b(0,$c,$18,$38,$7c,$54,$7c,$28,$38,$38,$3c)


dta b($7e,$fb,$b9,$38,$38,$3c,$1c,$1c,$e,6,3,0)

Zmiana pozycji poziomej. Za pozycje poziomą akurat odpowiadają rejestry komputerka, więc jest to proste jak w w/w przykładzie. Zmiana
pozycji pionowej. Obszar na naszego sprita jest 256 lub 128 bajtowy, każdy bajt odpowiada za jedną linię sprita. Żeby sprawić aby poruszał
sie on w pionie, nalezy przemieścić dane opisujące kształt w tym 256 lub 128 bajtowym obszarze.

ldy #0 ;pozycja Y obiektu


jsr poz_Y
rts

poz_Y ldx #0
petla lda shape,x ;przepisujemy 25 bajtow z tablicy `SHAPE` pod adres `pm+$400+py`
sta pm+$400+py,y ;w tej tablicy znajduja sie dane opisujace ksztalt duszka
iny
inx
cpx #$19
bne petla
rts

Jeśli zmieniamy pozycje pionową o znaczną wartość musimy wówczas wymazać całego sprita, inaczej pozostaną fragmenty po poprzedniej
pozycji (np. kursor myszki). Jeśli jednak zwiększamy go o 1 wystarczy że umieścimy wartość $00 na początku i końcu tablicy opisującej
kształt sprita (tak jak w w/w `shape`).

cls ldx #0
txa
cls_ sta pm+$400,x ;cały obszar 256 lub 128 kasujemy, wpisując $00
inx
* cpx #128
bne petla
rts

IN-OUT

Zabawy ze stacją dysków. Ze stacją dysków ponieważ nikt już nie powinien używac magnetofonu, nie wolno ;). Zatem powinniśmy
dowiedzieć się jak otworzyć plik, jak go odczytać, zapisać, skasować oraz jak wyswietlić katalog dyskietki. Nie będziemy zajmowali sie
dyskowym systemem Sparta ani MyDos, tylko tym najpopularniejszym DosII+ :). DOSII+ oraz jak mu mu podobne charakteryzuje sie
obsługą max 64 plików w katalogu dyskietki, brakiem podkatalogów, ślicznym CommandProcesorem. Ten CommandProcesor oznacza że
wpisujemy 3literowy taki prawie mnemonik, po nim parametry, naciskamy Return i mamy `bum`. Jeśli nasz program zabawia się ze stacją
dysków, a my nie uruchomiliśmy go spod DOSa, to coś z nami jest nie tak :). DOS musi być, chyba że chcecie napisać własny `Disk
Operating System`.

A dlaczego gry i inne programy wczytują się same, bez Dos-u ?

Nic nie wczytuje się samo, nasz `Operating System` ma w sobie programiki, które potrafią odczytać wskazany sektor z dysku. W czasie
bootowania `OPTION+RESET` system odczytuje pierwsze 3sektory i uruchamia w nich zawarty programik. Programik ten przejmuje
dalsze działania i ładuje do pamięci cały program. Właśnie tam umieszczone są procedury zabezpieczające przed kopiowaniem. Np. pewien
sektor na dysku jest uszkodzony, procedurka sprawdza czy rzeczywiście ten konkretny sektor nie daje sie odczytać jeśli tak tzn. że to jest
niby oryginał. Tyle że pewne stacje potrafiły kopiować te zabezpieczenia zapisując właśnie `pseudo bad sectory`. Do dyspozycji mamy
gęstości zapisy Single-90kb(128b sector), Enhanced-128kb (128b sector), Double Density-180kb (256b sector). Dwie pierwsze już nie
stosujemy bo po co. Używamy najczęściej DD. W przypadku stacji Toms720, Karin itp. możemy zapisywać dyski z gęstością 360kb, 720kb
itp w zależności od stacji. Po tym wstępie, przykłady odczytu sektora z pomocą OS i operacje z udziałem DOS-u. Bez udziału DOS-u,
pełny przykład w załączniku, plik `LOADER.ASM`:

ldx <$169 ;nr sektora, w naszym przykladzie jest to pierwszy sektor z katalogiem dysku
ldy >$169
stx $30a
sty $30b

lda #$52 ;kod operacji, $52 = READ


ldx <128 ;dlugosc sektora
ldy >128
sta cod+1 ;modyfikujemy odpowiednie komorki pamieci
stx lng+1
sty lng+6

ldx <bufor ;bufor pod ktory wczytane zostana dane


ldy >bufor

sio stx $304 ;< adres bufora


sty $305 ;> adres bufora
cod lda #0 ;kod operacji
sta $302
lda #$40 ;$40 odczyt
sta $303
lng lda #0 ;< dlugosc bufora
sta $308
lda #0 ;> dlugosc bufora
sta $309
jsr $e459 ;wywolanie procedury
rts

W celu poznania operacji wejścia/wyjścia (in-out) musimy zaznajomić się z podprogramem CIO, który może sterować 8-oma
urządzeniami. Zawiera on 8 jednakowych bloków IOCB (o długości 16 bajtów) umieszczonych w pamięci Atari od adresu $340:

IOCB0 $0340
IOCB1 $0350
IOCB2 $0360
IOCB3 $0370
IOCB4 $0380
IOCB5 $0390
IOCB6 $03A0
IOCB7 $03B0

Dla wszystkich IOCB struktura jest jednakowa:

0 ICHID HANDLER ID
1 ICDNO NUMER URZĽDZENIA
2 ICCMD COMMAND
3 ICSTA STATUS
4 ICBAL BUFFERADRL (bajt młodszy)
5 ICBAH BUFFERADRH (bajt starszy)
6 ICPTL PUTADR L (bajt młodszy)
7 ICPTH PUTADR H (bajt starszy)
8 ICBLL BUFFERLEN L (bajt młodszy)
9 ICBLH BUFFERLEN H (bajt starszy)
A ICAX1 AUX1
B ICAX2 AUX2
C ICAX3
D ICAX4
E ICAX5
F ICAX6

- HANDLER ID wartość określana przez podprogram CIO (związana z tablicą HATABS)


- COMMAND zawiera numer rozkazu, który ma wykonywać CIO
- STATUS zawiera rezultat operacji, tzn czy się udała czy nie
- BUFFERADR zawiera adres bufora danych
- PUTADR używany wewnętrznie przez podprogram CIO
- BUFFERLEN zawiera liczbę bajtów, które będą przekazywane przez wejście/wyjście
- AUX1 określana przez użytkownika: $04 - dla operacji czytania, $08 dla operacji zapisu
- AUX2 określana przez użytkownika, np. $80 dla operacji z magnetofonem (długość rekordów)

Aby uruchomić procedury CIO należy:


- wypełnić odpowiednie pozycje IOCB
- do rejestru X wprowadzić numer wybranego IOCB pomnożony przez 16
- wywołać procedurę CIO (jsr $e456)
- sprawdzić status operacji
- zamknąć kanał komunikacji (IOCB)

Po tej dawce teorii czas na przykłady:

iocb equ $340


ciov equ $e456

*--------
*- OPEN
*--------
open equ *

ldx #1*16 ;nr kanału * 16


lda #$03 ;komenda $03 = OPEN, otwiera kanał komunikacji
sta iocb+2,x

lda #$04 ;kod operacji, $04 = READ, otwarty kanał bedzie służył do czytania danych
sta iocb+10,x

lda <nam ;adres, pod którym umieszczona jest pierwsza litera urzadzenia, lub zbioru
sta iocb+4,x ;np. nam dta c`D:PLIK.OBX`,b($9b)
lda >nam ;zakończona EOL-em = $9b
sta iocb+5,x

jsr ciov ;wywołanie podprocedury systemowej CIOV


bmi error ;bit 7 ustawiony czyli coś niedobrego stało się z transmisją
rts

nam dta c`D:PLIK.OBX`,b($9b)

*---------
*- CLOSE
*---------
close equ *
ldx #1*16 ;nr kanału * 16
lda #$0c ;kod operacji $0C = CLOSE, zamyka kanał transmisji
sta iocb+2,x

jsr ciov ;i już standardowo, wywołanie CIOV


bmi error ;reakcja na błąd
rts ;koniec

*--------
*- READ
*--------
read equ *

ldx #1*16 ;nr kanału * 16


lda #$07 ;kod operacji $07 = GET BYTES, pobiera bajty
sta iocb+2,x

lda <bufor ;adres początku bufora danych, pod ten adres będą one wczytywane
sta iocb+4,x
lda >bufor
sta iocb+5,x

lda <length ;liczba bajtów do pobrania


sta iocb+8,x
lda >length
sta iocb+9,x

jsr ciov
bmi error
rts

*---------
*- WRITE
*---------
read equ *

ldx #1*16 ;nr kanału * 16


lda #$0b ;kod operacji $0B = PUT BYTES, zapisuje bajty
sta iocb+2,x

lda <bufor ;adres początku bufora danych, spod tego adresu będą one zapisywane
sta iocb+4,x
lda >bufor
sta iocb+5,x

lda <length ;liczba bajtów do zapisania (przesłania do urządzenia)


sta iocb+8,x
lda >length
sta iocb+9,x

jsr ciov
bmi error
rts

Kod $06 to DIRECTORY, czyli odczyt katalog dyskietki. Pozostałe 2 kody, są chyba najrzadziej używane. To GET RECORD = $05 i PUT
RECORD = $09. Powodują one wczytanie lub zapisanie bajtów aż do momentu wystąpienia znaku końca zbioru (EOL=$9b). Ich
wywołanie wygląda analogicznie jak w/w READ i WRITE.

You might also like