Professional Documents
Culture Documents
Kurs Assemblera 05
Kurs Assemblera 05
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:
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
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:
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
-*-
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:
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:
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
ldy #100
l0 tya
sta $d40a
sta $d01a
dey
bne l0
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
;i w tym momencie niejako działa już sam CPU, wszystko inne zamarło :)
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
ldx #8
col lda 704,x ;przepisujemy kolory z cieni do ich rejestrow właściwych
sta $d012,x
dex
bpl col
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:
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
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
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.
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:
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
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:
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
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.
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`.
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
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
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
*--------
*- OPEN
*--------
open equ *
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
*---------
*- CLOSE
*---------
close equ *
ldx #1*16 ;nr kanału * 16
lda #$0c ;kod operacji $0C = CLOSE, zamyka kanał transmisji
sta iocb+2,x
*--------
*- READ
*--------
read equ *
lda <bufor ;adres początku bufora danych, pod ten adres będą one wczytywane
sta iocb+4,x
lda >bufor
sta iocb+5,x
jsr ciov
bmi error
rts
*---------
*- WRITE
*---------
read equ *
lda <bufor ;adres początku bufora danych, spod tego adresu będą one zapisywane
sta iocb+4,x
lda >bufor
sta iocb+5,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.