Blitter to jednostka wyspecjalizowana w przesuwie (ang. shift) danych zlokalizowanych w pamięci graficznej CHIP komputera Amiga oraz rysowaniu odcinków. Blitter potrafi również - równocześnie podczas swojej głównej pracy - wypełniać obszary na kilka sposobów.
Ten artykuł traktuje tylko o przesuwie danych. O rysowaniu odcinków i wypełnianiu będą odrębne artykuły.
Termin "przesuw danych" to uogólnione określenie działania Blittera. Blitter wyśmienicie nadaje się m.in. do operacji rysujących polegających na kopiowaniu prostokątnych obiektów, również o nieregularnym kształcie.
Jak się wkrótce przekonasz wszystkie te funkcje będą niezbędne do wykonania podstawowych czynności rysujących.
Każdy z kanałów może wskazywać na inną mapę bitową (ang. bitmap), z których każda może mieć inną szerokość (liczbę bajtów w wierszu - ang. bytes per row) oraz wysokość. Jak wiadomo pamięć graficzna w Amidze składa się z bitplanów, gdzie każdy bit odpowiada za jeden piksel na ekranie. Blitter pracuje na jednostkach pamięci zwanych słowami, gdzie każde słowo to 16 bitów, czyli 16 pikseli.
Blitter może wykonać dowolną operację spośród 256 możliwych. Każdy typ operacji polega na alternatywie bitowej (OR) wybranych koniunkcji bitowych (AND) wszystkich trzech kanałów, bądź ich negacji bitowej (NOT). Oto tabela przedstawiająca poszczególne kombinacje. Kreska nad literą kanału oznacza negację bitową tego kanału:
Kanał | Bit minterm | Koniunkcja kanałów | ||||
---|---|---|---|---|---|---|
A | B | C | ||||
0 | 0 | 0 | 0 | A | B | C |
0 | 0 | 1 | 1 | A | B | C |
0 | 1 | 0 | 2 | A | B | C |
0 | 1 | 1 | 3 | A | B | C |
1 | 0 | 0 | 4 | A | B | C |
1 | 0 | 1 | 5 | A | B | C |
1 | 1 | 0 | 6 | A | B | C |
1 | 1 | 1 | 7 | A | B | C |
Obliczenie wartości minterm polega na ustawieniu bitów z odpowiedniej kolumny tabeli. Nas najbardziej będzie interesować następująca funkcja logiczna: AB+AC nazywana po angielsku cookie-cut. Jak wyznaczyć jej wartość minterm? Otóż należy włączyć pozostałe kanały do funkcji, ale tak, by nie wpływały na wynik, czyli w następujący sposób:
AB+AC=AB(C+C)+A(B+B)C= ABC+ABC+ABC+ABC. Teraz ustawiamy bity odczytane z tabeli: %11001010 binarnie = $CA szesnastkowo.
Dlaczego ta operacja jest najciekawsza? Otóż pozwala ona kopiować obiekt o dowolnym kształcie z dowolnej pozycji do dowolnej innej pozycji tak, by tło nie zostało zamazane. Wówczas:
Najpierw warto nauczyć się podstawowych rejestrów Blittera. Blitter posiada szereg rejestrów, za pomocą których możemy sterować jego działaniem.
Żeby wskazać Blitterowi skąd ma pobrać dane i gdzie umieścić wynik, ustawiamy rejestry BLTxPT, gdzie x to A, B, C i D. Każdy taki rejestr składa się z dwóch słów (opisujących jeden adres pamięci CHIP), ale możemy go załadować za pomocą jednej instrukcji procesora lub dwóch instrukcji koprocesora graficznego Coppera. Każdy adres jest parzysty, gdyż odnosi się do 16-bitowych słów.
Nie musimy ustawiać wszystkich adresów, ponieważ kanały można włączać lub wyłączać. Gdy kanał jest wyłączony, dane nie są z niego pobierane. Jeśli bierze on jednak udział w operacji logicznej, wówczas brana jest jedna wartość stała, którą należy umieścić w rejestrach BLTxDAT po załadowaniu rejestrów kontrolnych (o rejestrach kontrolnych jest mowa poniżej).
W rejestrach kontrolnych umieszcza się m.in. wartość minterm funkcji logicznej, ale też przesunięcia i flagi sterujące.
Żeby rozpocząć pracę Blittera należy ustawić rozmiar operacji. Służy do tego rejestr BLTSIZE oraz dwa rejestry dostępne od wersji ECS chipsetu BLTSIZV (wysokość) i BLTSIZH (szerokość). Szerokość podaje się w 16-bitowych słowach, zaś wysokość w wierszach.
Poniżej opisuję sposób użycia Blittera w poszczególnych przypadkach. Wszystkie przykłady w tym dziale dotyczą operacji na obszarach nie pokrywających się (nie nachodzących na siebie). W przypadku gdy obszary mają część wspólną, a bitmapa docelowa ma adres wyższy niż bitmapa źródłowa, wówczas należy skorzystać z trybu Descending, w której Blitter działa "od tyłu". Tryb ten zostanie opisany później.
Jako pierwszy przykład podam proste kopiowanie kanału A do kanału D bez przesunięcia i maskowania.
A=A(B+B)(C+C)=ABC+ABC+ABC+ABC. Wartość funkcji logicznej: %11110000 binarnie = $F0 szesnastkowo.
Na początek ustawimy rejestry kontrolne Blittera: BLTCON0 i BLTCON1. Pierwszy z nich odpowiada m.in. za włączenie kanałów. Ustawmy w nim flagi SRCA i DEST odpowiadające odpowiednio za włączenie kanału źródłowego A i kanału docelowego D. W ten rejestr wpisujemy też wartość przesunięcia (w naszym przykładzie 0) oraz minterm, czyli w naszym przypadku $F0 (lub alternatywę stałych symbolicznych ABC | ANBC | ABNC | ANBNC). Do drugiego rejestru kontrolnego wpisujemy w tej chwili wartość $0000.
Ten tryb najlepiej spisuje się, gdy mamy do wklejenia ikony o szerokości będącej wielokrotnością liczby 16. Rejestr adresu kanału źródłowego A BLTAPT ustalamy na adres pierwszego słowa ikony, zaś adres kanału docelowego D BLTDPT na adres pierwszego słowa miejsca, w który chcemy ikonę wkleić.
Następnie musimy ustalić tzw. modulo. Co to jest? Otóż obszary objęte operacją, na które wskazują adresy kanałów mogą być częścią większej całości. Blitter musi wiedzieć o ile bajtów ma zwiększyć adres by dostać się do następnego wiersza z danymi. Jeżeli znamy wartość bytes per row mapy bitowej to wystarczy odjąć od niej szerokość operacji w słowach pomnożoną przez 2, a następnie wstawić tą wartość do odpowiedniego rejestru BLTxMOD, gdzie x to A, B, C i D. Rejestry masek pierwszego (BLTAFWM) i ostatniego (BLTALWM) słowa kanału A ustawiamy na wartości standardowe, czyli $FFFF.
Poniższa tabela obrazuje zagadnienie wyznaczania wartości modulo. W tym przypadku jako szerokość operacji należy wstawić 1 (szerokość operacji liczymy w słowach), zaś jako modulo: 2 słowa * 2 bajty = 4 (modulo liczymy w bajtach). Suma modulo i szerokości ikony daje nam szerokość mapy bitowej. Żeby rozpocząć operację rysowania wpisujemy najpierw wysokość operacji (ilość wierszy objętych operacją) do rejestru BLTSIZV, a następnie długość jednego wiersza danych, objętego operacją kopiowania do BLTSIZH (w naszym przypadku wstawiamy tu liczbę 1).
Szerokość mapy bitowej = 3 słowa = 6 bajtów | |||
---|---|---|---|
Szerokość ikony = 1 słowo | Modulo = 2 słowa = 4 bajty | ||
Pierwsze słowo | Drugie słowo | Trzecie słowo | |
0101 0110 0011 1000 | 0001 0000 1110 1011 | 0110 1110 0101 1111 |
Oto przykład ustawiania rejestrów kontrolnych przez procesor dla kopiowania ikon w języku Asembler:
INCLUDE "hardware/custom.i" INCLUDE "hardware/blit.i" LEA $DFF000,A0 ; Baza rejestrów MOVE.W #($0<<ASHIFTSHIFT)!SRCA!DEST!ABC!ANBC!ABNC!ANBNC,bltcon0(A0) ; Ustawiamy pierwszy rejestr kontrolny MOVE.W #($0<<BSHIFTSHIFT),bltcon1(A0) MOVE.W #$FFFF,D0 MOVE.W D0,bltafwm(A0) MOVE.W D0,bltalwm(A0) |
Teraz przyszła pora na zasadnicze zagadnienie: kopiowania dowolnych obiektów niekoniecznie dosuniętych do granicy 16-bitowej. Istnieje tutaj kilka sytuacji. Postaram się jasno przedstawić zasady postępowania w tych sytuacjach. Użyjemy opisanej wcześniej, uniwersalnej operacji cookie-cut. Najważniejsze to ustalić:
Podam przykłady i postaram się je zilustrować. Weźmy taki oto wiersz obiektu oraz maskę i sprawdźmy na nim różne warianty. Warto zauważyć, że bity zapalone w obiekcie zawierają się w masce. To normalne, bo tam gdzie bity maski są zgaszone, stamtąd nie będzie brana grafika obiektu, tylko tło.
Maska | ||
---|---|---|
0000 0110 1100 1111 | 1100 0101 0000 1000 | 1110 0101 0110 1110 |
Obiekt | ||
0000 0010 0100 0110 | 1000 0001 0000 1000 | 0110 0100 0010 1100 |
Cel | ||
**** **** **** **** | **** **** **** **** | **** **** **** **** |
W najprostszym przypadku, gdy kopiujemy dane, które rozpościerają się na tyle samo bajtów w źródle jak i miejscu docelowym możemy wyrzucić niechciane bity ze źródła. Przykładowo chcemy skopiować dane od bitu nr 4 (licząc od lewej od jedynki) pierwszego słowa źródła do bitu nr 2 drugiego słowa źródła włącznie (szerokość: 15 pikseli) w miejsce rozpoczynające się od 10 bitu pierwszego słowa celu. Oznakujmy te informacje w tabeli:
Maska | ||
---|---|---|
0000 0110 1100 1111 | 1100 0101 0000 1000 | 1110 0101 0110 1110 |
Obiekt | ||
0000 0010 0100 0110 | 1000 0001 0000 1000 | 0110 0100 0010 1100 |
Cel | ||
**** **** **** **** | **** **** **** **** | **** **** **** **** |
Ażeby osiągnąć obrany cel musimy zamaskować niechciane bity i przesunąć dane. Oto lista czynnośći, jakie należy podjąć:
Oto wynik operacji. Gwiazdką symbolizowane są bity tła:
Wynik operacji | ||
---|---|---|
**** **** ***0 1*01 | **01 1010 **** **** | **** **** **** **** |
W tej sytuacji bity celu znajdują się na lewo od bitów źródła. O ile zdradzę, że w trybie pracy Descending Blitter może przesuwać w lewo, o tyle w tym przypadku, jeżeli chcemy zachować tryb pracy Ascending (z różnych względów) należy zastosować następującą metodę. Chcemy skopiować dane od piksela nr 9 do piksela nr 15 drugiego słowa źródła (szerokość 7 pikseli) w miejsce rozpoczynające się od bitu nr 1 drugiego słowa celu:
Maska | ||
---|---|---|
0000 0110 1100 1111 | 1100 0101 0000 1000 | 1110 0101 0110 1110 |
Obiekt | ||
0000 0010 0100 0110 | 1000 0001 0000 1000 | 0110 0100 0010 1100 |
Cel | ||
**** **** **** **** | **** **** **** **** | **** **** **** **** |
Wynik operacji | ||
---|---|---|
**** **** **** **** | **** 1*** **** **** | **** **** **** **** |
Pozostała nam taka oto ciekawa sytuacja. Jak widać cel zajmuje jedno słowo, podczas gdy dane źródłowe obejmują dwa słowa. W takim przypadku ustawiamy, zgodnie z dotychczas opisanymi zasadami maski pierwszego i ostatniego słowa kanału A, przesunięcia ustalamy na wartość 12 ($C). Za rozmiar bierzemy dwa słowa, jak również ustalamy początek kanału docelowego D na słowo bezpośrednio przed właściwym słowem celu.
Maska | ||
---|---|---|
0000 0110 1100 1111 | 1100 0101 0000 1000 | 1110 0101 0110 1110 |
Obiekt | ||
0000 0010 0100 0110 | 1000 0001 0000 1000 | 0110 0100 0010 1100 |
Wynik operacji | ||
**** **** **** **** | **** **** 0110 10** | **** **** **** **** |
Maska | ||
---|---|---|
0000 0110 1100 1111 | 1100 0101 0000 1000 | 1110 0101 0110 1110 |
Obiekt | ||
0000 0010 0100 0110 | 1000 0001 0000 1000 | 0110 0100 0010 1100 |
Wynik operacji | ||
**** **** **** **** | **** **** **** **** | **** **** **** **** |