Serwis Edukacyjny w I-LO w Tarnowie Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej
Autor artykułu: mgr Jerzy Wałaszek |
©2024 mgr Jerzy Wałaszek
|
Często ilość informacji, którą programista chce wyświetlić, przekracza ilość informacji, którą można zmieścić na ekranie. Jednym ze sposobów rozwiązania tego problemu jest przewijanie tej informacji przez ekran. Na przykład listingi programów w BASIC'u przewijają się pionowo od spodu do góry ekranu. Wszystkie komputery osobiste implementują ten typ przewijania. Jednakże komputer domowy ATARI posiada dwa dodatkowe rozwiązania dla przewijania, które oferują ekscytujące możliwości. Pierwszym jest zgrubne przewijanie przy pomocy instrukcji listy wyświetlania LMS (ang. Load Memory Scan – ładowanie treści z pamięci); drugim jest płynne przewijanie.
Zwykłe komputery używają przewijania zgrubnego, piksele przechowujące znaki mają ustaloną pozycję na ekranie, a tekst jest przesuwany przez przemieszczanie bajtów w obszarze pamięci RAM ekranu. Rozdzielczością przewijania jest pojedynczy piksel znakowy, co jest bardzo zgrubne. Tworzony przesuw jest skokowy i dosyć nieprzyjemny dla oka. Co więcej, osiąga się go przez przemieszczanie tysięcy bajtów w pamięci, co jest wolne i niezdarne. Ogólnie program musi przemieszczać dane na polu gry, aby były przewijane.
Niektóre komputery osobiste mogą tworzyć nieco bardziej płynne przesuwy przez rysowanie obrazków w trybie graficznym o wyższej rozdzielczości, a następnie przez przesuwanie tych obrazków. Chociaż osiąga się wyższą rozdzielczość przesuwania, to więcej danych musi być przemieszczane w celu otrzymania przesuwu i program jest w konsekwencji spowalniany. Podstawowym problemem jest to, iż to przewijanie jest implementowane przez przesuwanie danych przez obszar ekranu.
Jest lepszy sposób otrzymania zgrubnych przesuwów w komputerze ATARI 400/800: przesuwaj ekran ponad danymi. Kody instrukcji listy wyświetlania obsługują ładowanie danych linii skanowania z pamięci. Instrukcja LMS została po raz pierwszy opisana w rozdziale 2. Informuje ona ANTIC o położeniu pamięci ekranu. Zwykła lista wyświetlania będzie miała jedną instrukcję LMS na swoim początku; obszar RAM wskazany przez nią udostępnia dane ekranowe w sposób liniowy. Przez zmianę bajtów argumentu instrukcji LMS, można utworzyć prymitywny przesuw. W rezultacie spowoduje on przesuwanie okna pola gry ponad danymi ekranowymi. I tak, manipulując tylko dwoma bajtami adresowymi, możesz stworzyć efekt identyczny z przesuwaniem całej pamięci RAM ekranu. Poniższy program wykonuje to zadanie:
10 DLIST=PEEK(560)+256*PEEK(561):REM Znajdź listę wyświetlania 20 LMSLOW=DLIST+4:REM Pobierz dolny bajt adresu argumentu LMS 30 LMSHIGH=DLIST+5:REM Pobierz górny bajt adresu argumentu LMS 40 FOR I=0 TO 255:REM Pętla zewnętrzna 50 POKE LMSHIGH,I 60 FOR J=0 TO 255:REM Pętla wewnętrzna 70 POKE LMSLOW,J 80 FOR Y=1 TO 50:NEXT Y:REM Pętla opóźniająca 90 NEXT J 100 NEXT I 110 GRAPHICS 0 |
← ←
Powyższy program przesuwa ekran poprzez całą przestrzeń adresową komputera. Na ekranie pojawia zawartość tej pamięci. Przewijanie jest niezgrabnym przewijaniem szeregowym łączącym przewijanie w poziomie z przewijaniem w pionie. Czyste przewijanie w pionie można osiągnąć przez dodawanie lub odejmowanie stałej wartości (długości wiersza w bajtach) od argumentu LMS. Poniższy program wykonuje to:
10 GRAPHICS 0 20 DLIST=PEEK(560)+256*PEEK(561) 30 LMSLOW=DLIST+4 40 LMSHIGH=DLIST+5 50 SCREENLOW=0 60 SCREENHIGH=0 70 SCREENLOW=SCREENLOW+40:REM Następny wiersz 80 IF SCREENLOW<256 THEN GOTO 120:REM Za daleko? 90 SCREENLOW=SCREENLOW-256:REM Tak, popraw wskaźnik 100 SCREENHIGH=SCREENHIGH+1 110 IF SCREENHIGH=256 THEN GRAPHICS 0: END 120 POKE LMSLOW,SCREENLOW 130 POKE LMSHIGH,SCREENHIGH 140 GOTO 70 |
↑ ↑
Czyste przewijanie w poziomie nie jest tak łatwe do zrobienia jak czyste przewijanie w pionie. Problemem jest to, iż pamięć ekranowa dla prostej listy wyświetlania jest zorganizowana szeregowo. Bajty danych ekranowych dla linii są rozciągnięte jeden za drugim w szeregu, a bajty następnej linii znajdują się bezpośrednio za bajtami poprzedniej. Możesz przesuwać te linie w poziomie przez przesuwanie wszystkich bajtów w lewo, co można zrobić przez zmniejszanie o 1 argumentu instrukcji LMS. Jednakże bajt leżący najbardziej na lewo w każdej linii zostanie przesunięty na pozycję najbardziej na prawo w następnej wyższej linii. Pierwszy z przedstawionych powyżej programów przykładowych ilustrował ten problem.
Rozwiązanie polega na rozszerzeniu obszaru danych ekranowych i rozbiciu ich na ciąg niezależnych obszarów poziomych linii. Rys.6-1 schematycznie ilustruje tę ideę:
Po lewej stronie mamy układ normalny. Jednowymiarowa szeregowa pamięć RAM jest ułożona w ciąg liniowy do utworzenia obszaru danych ekranowych. Na prawo mamy układ potrzebny do właściwego przewijania w poziomie. Pamięć RAM jest oczywiście wciąż jednowymiarowa i wciąż szeregowa, lecz teraz wykorzystuje się ją inaczej. RAM dla każdej linii poziomej rozciąga się znacznie dalej, niż może to pokazać ekran. Nie jest to przypadkowe; cała idea przewijania polega na tym, aby pozwolić programowi wyświetlić więcej informacji, niż może pomieścić ekran. Nie możesz pokazać tej całej dodatkowej informacji, jeśli nie przydzielisz pamięci na jej przechowywanie. Z tym układem możesz zaimplementować prawdziwe przewijanie w poziomie. Możesz przesuwać okno ekranu nad danymi ekranowymi bez niepożądanych przesunięć w pionie, które występowały w poprzednim podejściu.
Pierwszym krokiem implementacji czystego przewijania w poziomie jest określenie całkowitej długości linii poziomej i przydzielenie odpowiedniej ilości pamięci RAM. Następnie musisz napisać zupełnie nową listę wyświetlania z instrukcjami LMS w każdej linii trybu. Ta lista wyświetlania będzie oczywiście dłuższa niż zwykle, lecz nie ma powodu, abyś nie mógł napisać takiej listy wyświetlania. Jakich wartości użyjesz na argumenty LMS? Najwygodniej będzie użyć adresu pierwszego bajtu każdej poziomej linii danych ekranowych. Dla każdej linii trybu będzie jeden taki adres. Gdy nowa lista wyświetlania trafi na swoje miejsce, ANTIC musi się do niej zwrócić, a dane ekranowe muszą zostać zapisane, aby wypełnić ekran. Aby dokonać przewijania argumenty wszystkich instrukcji LMS na liście wyświetlania muszą zostać zwiększone o 1 dla przewinięcia w prawo lub zmniejszone o jeden dla przewinięcia w lewo. Program sterujący musi zagwarantować, aby obraz nie przewinął się poza granice przydzielonych obszarów pamięci RAM; w przeciwnym razie na ekranie pojawią się śmieci. Przy realizacji tego zadania programista musi pamiętać, iż argument instrukcji LMS wskazuje na pierwszy bajt danych ekranowych w wyświetlanej linii. Maksymalną wartością tego argumentu jest adres ostatniego bajtu w długiej linii poziomej minus liczba bajtów w wyświetlanej linii. Pamiętaj również, że wartość LMS w obrębie długości wyświetlanej linii (w bajtach) nie powinna wykraczać poza granicę bloku 4KB, w przeciwnym razie zostaną wyświetlone złe dane z powodu przewinięcia się licznika LMS.
Ponieważ proces ten jest nieco zawiły, wypracujmy przykład. Najpierw musimy wybrać naszą całkowitą długość linii w poziomie. Użyjemy linii poziomej o długości 256 bajtów, ponieważ uprości to obliczenia. Każda linia pozioma będzie zatem potrzebowała jednej strony pamięci RAM. Ponieważ użyjemy trybu BASIC 2, będzie 12 linii trybu na ekranie; stąd będziemy potrzebować 12 stron lub 3KB RAM. Dla prostoty (i zagwarantowania, że nasza pamięć ekranu będzie wypełniona niezerowymi danymi) użyjemy dolnych 3KB RAM. Obszar ten jest wykorzystywany przez system operacyjny i DOS, zatem powinien być pełen interesujących danych. Aby sprawy stały bardziej interesujące, umieścimy listę wyświetlania na stronie 6, abyśmy mogli wyświetlać ją w trakcie przewijania. W ten sposób początkowe wartości argumentów LMS będą szczególnie proste do wyliczenia; bajty dolne zawsze będą miały wartość zero, a bajty górne będą kolejno przyjmować 0, 1, 2, itd. Poniższy program wykonuje wszystkie te operacje i przewija ekran poziomo:
10 REM Najpierw tworzymy listę wyświetlania 20 POKE 1536,112:REM 8 pustych linii 30 POKE 1537,112:REM 8 pustych linii 40 POKE 1538,112:REM 8 pustych linii 50 FOR I=1 TO 12:REM Pętla umieszczająca na liście wyświetlania 60 POKE 1536+3*i,71:REM instrukcje trybu BASIC 2 z ustawionym bitem LMS 70 POKE 1536+3*i+1,0:REM Dolny bajt argumentu LMS 80 POKE 1536+3*i+2,i:REM Górny bajt argumentu LMS 90 NEXT I 100 POKE 1575,65:REM Instrukcja JVB dla ANTIC'a 110 POKE 1576,0:REM Lista wyświetlania rozpoczyna się od adresu $0600 120 POKE 1577,6 130 REM Informujemy ANTIC o położeniu listy wyświetlania 140 POKE 560,0 150 POKE 561,6 160 REM Teraz przewijamy poziomo 170 FOR I=0 TO 235:REM Pętla przez dolne bajty LMS 175 REM Używamy 235 a nie 255, ponieważ szerokość ekranu wynosi 20 znaków 180 FOR J=1 TO 12:REM w każdej linii trybu 190 POKE 1536+3*J+1,I:REM Wstawiamy do argumentu LMS nowy dolny bajt 200 NEXT J 210 NEXT I 220 GOTO 170:REM Pętla nieskończona |
Ten program przewija dane z prawa na lewo. Gdy zostaje osiągnięty koniec strony, zaczyna po prostu od jej początku. Listę wyświetlania można znaleźć w szóstym wierszu licząc od góry (ponieważ znajduje się ona na stronie 6 RAM). Pojawia się ona jako ciąg par znaków ujętych w apostrofy.
Następnym krokiem jest mieszanie przewijania pionowego z
poziomym, aby otrzymać przewijanie po przekątnej. Przewijanie
poziome jest otrzymywane przez dodawanie lub odejmowanie 1 od
argumentu LMS. Przewijanie pionowe jest otrzymywane przez dodawanie
lub odejmowanie długości linii od argumentu LMS. Przewijanie
przekątne otrzymamy przez wykonanie obu operacji. Istnieje cztery
możliwe kierunki przewijania przekątnego. Jeśli przykładowo długość
linii wynosi 256 bajtów i chcemy przewijać w dół i na prawo, musimy
dodawać
Jeśli w różny sposób manipulujemy bajtami LMS, to możliwe są wszelkie rodzaje dziwnych aranżacji ekranu. Linie mogą przewijać się jedne względem innych lub przeskakiwać ponad sobą. Oczywiście, niektóre z tych rzeczy można wykonać z normalnym ekranem, lecz niezbędne stanie się przemieszczanie większej ilości danych. Prawdziwą korzyścią z przewijania LMS jest prędkość. Zamiast manipulować całym ekranem danych o rozmiarze wielu tysięcy bajtów, program potrzebuje jedynie manipulować kilkoma tuzinami bajtów.
Inną ważną możliwością przewijania komputera jest płynne przewijanie, które oznacza możliwość przewijania piksela w krokach mniejszych niż rozmiar piksela. (W tym rozdziale pojęcie piksel odnosi się do całego znaku, nie do mniejszych punktów graficznych, z których jest utworzony). Zgrubne przewijanie wykonywane jest w krokach równych rozmiarowi piksela; przewijanie płynne wykonywane jest w krokach jednej linii skanowania w pionie i jednego taktu koloru w poziomie. Płynne przewijanie dotyczy tylko jednego piksela (znaku). Jeśli chcesz uzyskać płynne przewijanie na dłuższych dystansach, musisz sprząc ze sobą przewijanie płynne i zgrubne.
Do implementacji płynnego przewijania potrzebne są tylko dwa kroki. Najpierw ustawiasz bity włączające płynne przewijanie w bajtach instrukcji listy wyświetlania dla linii trybu, w których chcesz mieć płynne przewijanie. (W większości przypadków chcesz przewijać cały ekran, zatem ustawiasz bity płynnego przewijania we wszystkich instrukcjach linii trybu na liście wyświetlania.) Bit D5 instrukcji listy wyświetlania uaktywnia przewijanie w pionie; bit D4 instrukcji listy wyświetlania uaktywnia przewijanie w poziomie. Następnie wpisujesz wartość przesuwu do odpowiedniego rejestru przewijania. Są dwa takie rejestry, jeden dla przewijania poziomego i jeden dla przewijania pionowego. Rejestr przewijania poziomego (HSCROL) znajduje się pod adresem $D404; rejestr przewijania pionowego (VSCROL) jest pod adresem $D405. Przy przewijaniu w poziomie umieszczasz w rejestrze HSCROL liczbę taktów koloru, o które chcesz przesunąć linię trybu. Przy przewijaniu pionowym w rejestrze VSCROL umieszczasz liczbę lini skanujących, o które chcesz przesunąć linię trybu. Te wartości przewijania zostaną zastosowane we wszystkich liniach, w których uaktywniono odpowiednie przewijanie płynne.
Są dwa czynniki komplikujące przewijanie płynne. Oba pochodzą stąd, iż częściowo przewinięty ekran pokazuje więcej informacji niż ekran normalny. Rozważ przykładowo, co się stanie, gdy przesuniesz jakąś linię o połowę znaku w lewo. W linii tej znajduje się 40 znaków. Połowa pierwszego znaku znika poza krawędzią ekranu. 40-ty znak przesuwa się w lewo. Co zajmie jego miejsce? Połowa nowego znaku powinna wsunąć się na miejsce, które powstało po przesunięciu 40-tego znaku. Ten znak będzie 41-szym znakiem. Lecz w zwykłej linii jest tylko 40 znaków. Co się stanie?
Jeśli stosowałeś przewijanie zgrubne, to 41-szy znak nagle pojawiał się na ekranie, gdy pierwszy znak zniknął poza lewą krawędzią. To nagłe pojawienie się nerwowe i brzydkie. Rozwiązanie tego problemu zostało już wbudowane w sprzęt. Istnieją trzy opcje wyświetlania dla szerokości linii: wąskie pole gry (szerokość 128 taktów koloru), normalne pole gry (szerokość 160 taktów koloru) i szerokie pole gry (szerokość 192 takty koloru). Opcje te są wybierane przez ustawienie odpowiednich bitów w rejestrze DMACTL. Gdy używa się płynnego przewijania w poziomie, ANTIC automatycznie pobiera więcej danych z RAM niż jest wyświetlane. Na przykład, jeśli DMACTL jest ustawiony na normalne pole gry, które w trybie 0 BASIC'a ma 40 bajtów na linię, to ANTIC faktycznie pobierze dane o rozmiarze szerokiego pola gry — 48 bajtów na linię. Jeśli nie zostanie to uwzględnione, to linie będą przesunięte poziomo. Problem ten nie ujawni się, jeśli już zorganizowałeś pamięć RAM ekranu w długie linie poziome jak na rys.6-1.
Z podobnym problemem przy przewijaniu pionowym można poradzić sobie na dwa sposoby. Niechlujnym sposobem jest zignorowanie problemu. Wtedy nie otrzymasz półobrazów na obu końcach ekranu. Obrazy na spodzie ekranu nie będą przewijane prawidłowo, będą się nagle pojawiać na ekranie. Poprawne rozwiązanie wymaga nieco pracy. Aby otrzymać właściwe przewijanie płynne przy wchodzeniu linii na ekran i przy wychodzeniu z niego, musisz przeznaczyć jedną linię trybu na bufor. Wykonujesz to przez wstrzymanie się od ustawienia bitu przewijania pionowego w instrukcji listy wyświetlania dla ostatniej linii trybu obszaru przewijanego w pionie. Okno będzie się teraz przewijało bez nieprzyjemnych szarpnięć. Obraz ekranowy zostanie skrócony o jedną linię trybu. Teraz staje się widoczna zaleta przewijanych ekranów. Możliwe jest stworzenie obrazów ekranowych, które zawierają więcej niż 192 linie skanowania na ekranie. To doprowadziłoby do katastrofy przy statycznym ekranie, lecz przy przewijanym ekranie fragmenty obrazów, które znajdują się poniżej lub ponad ekranem zawsze można przewinąć, tak aby znalazły się na widoku.
Płynne przewijanie będzie działać tylko do tego miejsca. Granicą przesunięcia w pionie jest 16 linii skanowania; granica pozioma wynosi 16 taktów koloru. Jeśli spróbujesz wyjść poza te granice, to ANTIC po prostu zignoruje starsze bity rejestrów przewijania. Aby otrzymać pełne przewijanie (w którym cały ekran płynnie przewija się tak daleko, jak sobie życzysz), musisz sprząc ze sobą przewijanie płynne z przewijaniem zgrubnym. Aby to zrobić, najpierw przesuwaj obraz płynnie, pamiętając jak daleko został przewinięty. Gdy ilość płynnego przesuwu zrówna się z rozmiarem piksela, zresetuj rejestr płynnego przesuwu i wykonaj przesuw zgrubny. Rys.6-2 ilustruje ten proces.
To jest jeden krok | |||||||||
pozycja startowa |
pojedynczy przesuw płynny |
podwójny przesuw płynny |
potrójny przesuw płynny |
poczwórny przesuw płynny |
pięcio- krotny przesuw płynny |
sześcio- krotny przesuw płynny |
siedmio- krotny przesuw płynny |
powrót na pozycję startową |
przesuw zgrubny |
Poniższy program ilustruje proste przewijanie płynne:
1 HSCROL=54276 2 VSCROL=54277 10 GRAPHICS 0:LIST 20 DLIST=PEEK(560)+256*PEEK(561) 30 POKE DLIST+10,50:REM Włączenie obu rodzajów przewijania 40 POKE DLIST+11,50:REM Dla dwóch linii trybu 50 FOR Y=0 TO 7 60 POKE VSCROL,Y:REM Przewijanie w pionie 70 GOSUB 200:REM Opóźnienie 80 NEXT Y 90 FOR X=0 TO 3 100 POKE HSCROL,X:REM Przewijanie w poziomie 110 GOSUB 200:REM Opóźnienie 120 NEXT X 130 GOTO 40 200 FOR J=1 TO 200 210 NEXT J:RETURN |
Ten program pokazuje płynne przewijanie wykonywane z bardzo małą prędkością. Demonstruje on również kilka problemów, które pojawiają się przy przewijaniu. Po pierwsze, linie wyświetlane poniżej przewijanego okna są przesunięte na prawo. Spowodowane jest to tym, iż ANTIC automatycznie pobiera 48 bajtów na linię zamiast 40. Problem ten pojawia pojawia się jedynie w nierealistycznych programach demonstracyjnych, jak ten tutaj. W rzeczywistych zastosowaniach przewijania odpowiednie ułożenie danych ekranowych (jak pokazano na rys.6-1) uniemożliwia pojawienie się takiego problemu. Po drugie, poważniejszy problem pojawia się, gdy rejestry przewijania są modyfikowane, gdy ANTIC jest w połowie swojego procesu wyświetlania. Zakłóca to działanie ANTIC'a i powoduje drganie obrazu. Rozwiązaniem jest zmiana zawartości rejestrów przewijania jedynie podczas okresu wygaszania pionowego. Można to osiągnąć jedynie w programach asemblerowych. Z tego powodu przewijanie płynne zwykle wymaga zastosowania języka asemblera..
Istnieją liczne zastosowania w grafice dla w pełni płynnego przewijania. Oczywistym zastosowaniem jest wykorzystanie go przy dużych mapach utworzonych w grafice znakowej. Używając trybu graficznego 2 BASIC'a utworzyłem bardzo dużą mapę Rosji, która zawierała zawartość około 10 ekranów obrazkowych. Ekran staje się oknem na tą mapę. Użytkownik może przemieszczać ekran po całej mapie za pomocą dżojstika. System ten jest bardzo oszczędny pamięciowo, cały program mapy z danymi i definicjami zestawu znaków zajmuje w całości około 4KB pamięci RAM.
Istnieje wiele innych zastosowań tej techniki. Każdy bardzo duży obrazek, który można narysować w grafice znakowej, da się wykorzystać w tym systemie. (Przewijanie nie wymaga grafiki znakowej. Grafika pikselowa jest mniej pożądana w zastosowaniach przewijania z uwagi na duże wymagania pamięciowe.) W ten sposób można prezentować duże schematy elektroniczne. Dżojstik mógłby zostać użyty do zarówno przewijania schematu jak i zaznaczania na nim określonych komponentów, nad którymi użytkownik chce wykonać jakąś operację. Tą techniką można również wyświetlać duże plany budowlane. Każdy duży obrazek, który nie potrzeba pokazywać w całości, można przedstawić za pomocą tego systemu.
Duże bloki tekstu również można tutaj zastosować, chociaż może być niepraktycznym czytanie ciągłych bloków tekstu przez przewijanie obrazu. Ten system jest bardziej przystosowany do prezentowania bloków niezależnego tekstu. Jednym ze szczególnie ekscytujących pomysłów jest zastosowanie tego systemu do menu. Program startuje prezentując na ekranie znak powitalny ze znakami oznaczającymi różne podmenu wskazujące na inne regiony większego obrazu. Użytkownik przewija menu za pomocą dżojstika i czyta jego opcje. Gdy zechce dokonać wyboru, umieszcza kursor na opcji i naciska czerwony przycisk. Chociaż tego systemu nie można zastosować we wszystkich programach, to jednak w niektórych z nich mógłby być cennym dodatkiem.
Dżojstik
Istnieją dwa "przyszłościowe" zastosowania, które nie zostały jeszcze wciąż w pełni zbadane. Pierwszym z nich jest wybiórcze przewijanie płynne, w którym różne linie trybu ekranu mają przesunięte różne kawałki. Zwykle chcesz przewijać cały ekran, lecz nie jest to konieczne. Mógłbyś sobie wybrać jedną linię do przewijania tylko w poziomie, inną do przewijania tylko w pionie, itd. Drugą przyszłościową cechą jest perspektywa zastosowania przerwań z listy wyświetlania do zmiany w locie rejestrów HSCROL lub VSCROL. Jednakże zmiana w locie rejestru VSCROL jest ryzykowną operacją; spowoduje prawdopodobnie zakłócenia w pracy ANTIC'a i otrzymasz niepożądany wynik. Zmienianie HSCROL jest również ryzykowne, lecz mogłoby być prostsze.
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2024 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone
pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.