Obraz w ZX81


Tekst i grafika znakowa

To jest standardowy tryb graficzny. Ekran składa się z 24 wierszy po 32 znaki w każdym. Znaki są tworzone w matrycy 8 × 8 pikseli. Jednakże użytkownik nie ma dostępu do pikseli tworzących kształt znaków, jedynie do 64 zdefiniowanych przez Sinclair'a znaków. Na szczęście niektóre z tych znaków składają się z bloków 4 × 4 piksele, co pozwala tworzyć grafikę o rozdzielczości 64 × 48 pikseli. Znaki literowe i graficzne można wyświetlać normalnie i w negatywie. Poniższy rysunek przedstawia wszystkie znaki ZX-81:

 

Pamięć obrazu

Pamięć obrazu jest adresowana przez wskaźnik D_FILE ($400C) w obszarze zmiennych systemowych ZX-81. Pierwszym bajtem w VRAM (ang. video RAM - pamięć przechowująca obraz wideo) jest kod instrukcji HALT ($76), za którym znajdują się dane (jeden bajt na znak) wszystkich 24 wierszy, każdy wiersz jest zakończony również kodem instrukcji HALT. Jeśli wiersz zawiera mniej niż 32 znaki, kod HALT powoduje wyświetlenie reszty wiersza na biało aż do krawędzi ekranu. (Dlatego tekst umieszczony po lewej stronie będzie zajmował mniej pamięci niż tekst wycentrowany lub dosunięty do prawego marginesu.)

 

Kody znakowe, rozmiar VRAM

Kody znakowe w zakresie od $00 do $3F (0 - 63) wyświetlają pozytywowe 64 znaki - czarne na białym tle. Znaki można obrócić w negatyw przez ustawienie w ich kodzie bitu nr 7, stąd kody $80 ... $BF (128...191) odnoszą się do znaków negatywowych.

W pełni rozwinięta pamięć VRAM obejmuje 793 bajty (32 ×24 + 25 kodów HALT, co prawie zajmuje całą pamięć wewnętrzną 1KB RAM), całkowicie zwinięty ekran to jedynie 25 bajtów (kody HALT).


Zestaw znaków

Zestaw znaków jest adresowany przez rejestr przerwań mikroprocesora I, którego zawartość przechowuje starszy bajt adresu. W ZX-81 matryce znaków umieszczone są w obszarze ROM od $1E00 do $1FFF. Zatem rejestr I przechowuje wartość $1E. Ustawienie tego rejestru na wartości od $40 do $7F, aby matryce znaków były pobierane z pamięci RAM a nie ROM, niestety nie funkcjonuje.

 

Techniczne szczegóły procedury wyświetlania

Dane ekranowe są mniej więcej "wykonywane" przez mikroprocesor. Gdy wyświetlany jest wiersz znaków na ekranie, BIOS pobiera adres pierwszego znaku, np. $4123, ustawia w nim bit nr 15, co zmienia $4123 na $C123, a następnie wykonuje skok pod adres $C123.

W tym momencie układy elektroniczne ZX-81 wykrywają, iż linia adresowa mikroprocesora A15 ma stan wysoki, a linia /M1 ma stan niski, co sygnalizuje odczyt kodu instrukcji maszynowej. W takiej sytuacji obszar $C000 - $FFFF jest zastępowany obszarem $4000-$7FFF. Kod "instrukcji" jest umieszczany jak zwykle na magistrali danych, układy wyświetlania interpretują go jako dane znaku, a jeśli kod ten ma wyzerowany bit nr 6, to magistrala danych jest zmuszana do przyjęcia stanu 0 na wszystkich liniach zanim mikroprocesor zdąży odczytać ten kod. W efekcie mikroprocesor odczytuje z magistrali danych instrukcję maszynową o kodzie 0, czyli NOP, i wykonuje ją. Bit nr 7 w skradzionym kodzie jest używany jako atrybut negatywu znaków. Bity 0-5 adresują jeden z 64 znaków w ROM pod adresem (rejestr I × $100 + char × 8 + linecntr). Bajt spod tego adresu jest ładowany do rejestru przesuwnego i bity są przesyłane kolejno do wyświetlacza z częstotliwością 6,5MHz (tj. 8 pikseli w ciągu 4 cykli zegarowych mikroprocesora - tyle zajmuje mu wykonanie instrukcji NOP).

Jednakże, gdy pojawi się kod instrukcji z ustawionym bitem nr 6, to układy wideo odrzucają go (wyświetlają białe piksele bez względu na ustawienie bitów nr 7 i nr 0 - 5) i nie wymuszają stanu 0 na magistrali danych, a mikroprocesor odczytuje kod instrukcji z magistrali danych i wykonuje go jak zwykle. Zwykle będzie to instrukcja HALT - przed wyświetlaniem wiersza znaków. BIOS uaktywnia przerwania i inicjalizuje rejestr R, który wyprodukuje przerwanie, gdy jego bit nr 6 zostanie wyzerowany.

W tym specjalnym przypadku rejestr R jest zwiększany z ustaloną szybkością 4 cykli mikroprocesora (dane wideo wykonywane jako instrukcje NOP, po których następują powtarzające się instrukcje HALT) tak, aby wyświetlanie linii zostało wstrzymane w ustalonym okresie czasu, bez względu na zwiniętą lub rozwiniętą długość danego wiersza znaków.

Powyżej wspomniano o dodatkowym rejestrze, zwanym linecntr (licznik linii), który używany jest do adresowania pionowych wierszy (0...7) w matrycy znaku. Rejestr ten jest zerowany w czasie pionowego wygaszania obrazu (gdy strumień elektronów rysujący obraz telewizyjny na kineskopie powraca z dołu do góry ekranu), a następnie zwiększany raz na linię skanującą obrazu. Stąd BIOS musi "wykonać" każdy wiersz znaków 8 razy, zanim zacznie "wykonywać" następny wiersz.

 

Typowa grafika znakowa ZX-81

 

Prawdziwi mistrzowie potrafią jednak osiągnąć "cuda" nawet w tak prymitywnym środowisku graficznym. Oto kilka próbek standardowej grafiki ZX-81:


Pseudo Grafika Hi-Res

Ta metoda jest wykorzystywana do wyświetlania grafiki o rozdzielczości 256 × 192 piksele, które są jednakże ograniczone do 128 kombinacji w obrębie grupy 8 bitów przypadających na wiersz matrycy znaku. Chociaż ten tryb nie jest wspierany przez BIOS, kilka gier z niego korzysta: Rock Crush, Dans Revenge, Rocketman, Forty Niner, Madjump II, Bipods, Micromouse i być może jeszcze inne.
W zasadzie działa on bardzo podobnie do trybu tekstowego, wysokość znaku jest zredukowana do do pojedynczej linii skanującej, zatem matryca znaku składa się z 8 × 1 pikseli, a nie jak standardowo 8 × 8 pikseli. Cały ekran składa się z 32 × 192 tych "płaskich" znaków. Każdy wiersz kończy się zwykle instrukcją RET o kodzie $C9. Obszar ekranu zajmuje zatem 6176 bajtów pamięci.

Do pracy tego trybu niezbędna jest specjalna procedura wyświetlania, która zmusza rejestr linecntr do zerowania się przez wysłanie bardzo krótkiego sygnału synchronizacji pionowej (równolegle ze sprzętowo generowanym sygnałem synchronizacji poziomej) przy każdej linii skanującej. W rezultacie wyświetlany jest z każdego znaku tylko górny wiersz matrycy. Ponieważ większość normalnych znaków ma ten wiersz pusty, należy zmienić na inny w ROM adres matrycy znaków, który przechowuje rejestr procesora I.

Przykładowo, ustawiając w rejestrze I wartość $0C, wybrany zostanie obszar od $0C00 do $0DFF (w krokach co 8: górny wiersz matryc znaków #0, #1, #2 pod adresami $C00, $C08, $C10, itd). Kody bajtów w tym obszarze pamięci będą następnie wykorzystywane jako predefiniowane grupy 8 pikseli, które mogą, lecz nie muszą odpowiadać potrzebom programisty. Każda z tych 64 grup pikseli może być wyświetlana w negatywie, jak normalne znaki tekstu, zatem teoretycznie mamy do dyspozycji 128 różnych grup 8 pikseli, praktycznie jest ich mniej, ponieważ część z bajtów powtarza się w tym obszarze pamięci.

Ponieważ standardowa procedura obsługi przerwań w BIOS pod adresem $0038, która wspomaga wyświetlanie obrazu, nie obsługuje opisanego powyżej sposobu wyświetlania znaków, program musi dostarczyć swój własny sterownik przerwań. Z tego właśnie powodu każdy wiersz zakończony jest kodem instrukcji RET, a nie kodem instrukcji HALT.

 

Pseudo grafika Hi-Res

 

Grafika True Hi-Res

Ten tryb produkuje ekran graficzny o rozdzielczości 256 × 192 piksele, i w przeciwieństwie do trybu Pseudo Hi-Res pozwala zmieniać oddzielnie każdy piksel. Wadą tego rozwiązania jest to, iż nie działa ono z większością modułów pamięci zewnętrznej RAM Paks (chociaż średnio zdolny użytkownik bez problemów jest w stanie przerobić taki moduł tak, aby się nadawał do trybu Hi-Res. Wystarczą dwie diody i rezystor).

Jednakże tryb ten działa z pamięcią wewnętrzną i zmodyfikowanymi modułami RAM Paks. Przy użyciu pamięci wewnętrznej obraz jest bardzo mały, gdyż tylko taki się w niej mieści (procedura wyświetlająca musi zwiększyć liczbę pustych linii w pionie oraz dodać marginesy w poziomie), a wtedy pamięć zewnętrzna powinna być odłączona, gdyż w przeciwnym razie zablokuje ona pamięć wewnętrzną RAM.
Grafika True Hi-Res wykorzystywana jest w grach Guus Flater, Starfight, i w kilku demach, takich jak WRX1K.

Podstawowa idea opiera się na przesunięciu zestawu znaków do RAM w obszar od $4000 do $7FFF przez ustawienie rejestru wektora przerwań I na $40...$7F. Teraz system wyświetlania nie działa w sposób oczekiwany, tj. jako tryb tekstowy. Zarówno wykonywany kod instrukcji (kod znaku) oraz wartość licznika linii linecntr są ignorowane. Zamiast nich piksele są pobierane bezpośrednio z pamięci spod adresu (IR). Każde 8 bitów z każdego bajtu reprezentuje 8 pikseli. Obraz jest zdefiniowany w postaci zwykłej, monochromatycznej bitmapy.

Dane tej bitmapy mogą znajdować się w dowolnym miejscu pamięci w RAM, a nie są one "wykonywane" jak w innych trybach wyświetlania, potrzebne są tylko surowe dane (nie ma potrzeby dołączania do każdego wiersza kodów instrukcji HALT lub RET). Zwróć uwagę, iż jedynie dolne 7 bitów rejestru odświeżania jest zwiększane przez mikroprocesor, zatem należy się zatroszczyć, aby licznik ten nie przewinął się wewnątrz linii. Na przykład, bitmapa szeroka na 256 pikseli (32 bajty) musi być wyrównana w pamięci do adresów podzielnych przez 32.

Główna procedura wyświetlająca powinna załadować starszy bajt adresu bieżącej linii bitmapy do rejestru przerwań I, młodszy bajt do rejestru A, a następnie skoczyć do procedury wyświetlania fikcyjnego ekranu D_FILE z ustawioną na 1 linią adresową A15. Ta procedura powinna skopiować rejestr A do rejestru R i wykonać 32 instrukcje NOP ($00 - bit 7 wyzerowany, co oznacza pozytywowe wyświetlanie, bit 6 wyłącza wymazywanie, dane są bezpośrednio odczytywane z (IR), zatem kod znaku w bitach B0-B5 jest ignorowany) i powrócić do głównej procedury - która następnie wprowadza nieco opóźnień, przygotowuje nowy adres w I oraz A i zaczyna cały proces od nowa z następną linią pikseli (wykorzystując tę samą procedurę ponownie), aż cały ekran graficzny zostanie wyświetlony.
 

Obraz w True Hi-Res na ZX-81


Wygaszanie obrazu i powrót plamki elektronów

Obraz staje się biały podczas okresu wygaszania. Tak dzieje się, gdy bit 15 rejestru licznika programu w mikroprocesorze (PC - ang. Program Counter) ma wartość 0 (procesor wykonuje program z obszaru pamięci poniżej $8000 - 32768) oraz gdy bit nr 6 bieżącego kodu instrukcji jest ustawiony na 1. Teoretycznie mikroprocesor mógłby wykonywać dowolny program podczas wygaszania - jednakże, skoro istnieje jedynie ograniczone sprzężenie zwrotne z przerwaniami, czas ten zwykle jest tracony na instrukcjach HALT lub innych pętlach opóźniających, które są niezbędne, aby zsynchronizować mikroprocesor z wyprowadzaniem danych wideo na ekran telewizora.

 

Wygaszanie pionowe

Górny i dolny margines ekranu jest wyświetlany ponad i poniżej aktualnego obrazu. Wysokość tych marginesów zależy od szybkości odświeżania ekranu. BIOS odczytuje szybkość odświeżania (50Hz w Europie - 60Hz w USA), gdy sprawdza naciśnięcia klawiszy, i używa jej do wyliczenia pożądanej wysokości marginesu dla każdej klatki obrazu. Dzięki tej cesze ZX-81 mógł być łatwo eksportowany do krajów o różnych systemach TV bez konieczności przebudowy.

 

Wygaszanie Pionowe w ZX-81 - tryb SLOW

Do wygaszania używane są przerwania niemaskowalne NMI (ang. Non Maskable Interrupts), które umożliwiają wykonywanie kodu programu podczas czasu trwania wygaszania. Przerwania NMI są uaktywniane przez porty we/wy, następnie sterownik NMI jest wywoływany przy każdej linii skanowania. Sterownik zwiększa licznik i (jeśli licznik się nie przewinie) wraca do programu użytkownika, w przeciwnym razie wykonuje instrukcję HALT, aby zsynchronizować w czasie mikroprocesor z wyświetlaniem z dokładnością co do jednego cyklu i przerywa procedurę wygaszania.


Wygaszanie poziome

W ZX-81 mikroprocesor nie może być wykorzystywany do wykonywania programów użytkownika podczas okresu wygaszania poziomego - musi on wykonywać ściśle wyliczone w czasie instrukcje, aby zachować synchronizację z układami generacji obrazu. Mogłoby to być uzyskiwane na drodze sprzętowej. Jednakże obraz wideo w ZX-81 musi obsługiwać wygaszanie o różnej długości, gdy stosowane są zwinięte w pamięci ekrany. W takich przypadkach prawy margines musi być zwiększany, gdy są wyświetlane puste lub niepełne wiersze. Osiąga się to przez załadowanie określonej wartości do rejestru odświeżania R (przed rozpoczęciem wyświetlania wiersza znaków) i zakończenie wiersza rozkazem HALT, który będzie wykonywany, aż bit nr 6 rejestru R osiągnie stan zero.
 

Powrót plamki elektronów

Podczas okresu powrotu poziomego/pionowego strumień elektronów rysujący obraz na ekranie telewizora powraca na początek linii skanowania/z dołu do góry ekranu. Powrót poziomy jest generowany przez układy elektroniczne, więc należy zatroszczyć się o to, aby procedura wyświetlająca była zsynchronizowana z sygnałami powrotu poziomego.

Powrót pionowy musi być zainicjowany i wyłączony przez operacje we/wy. Impuls powrotu pionowego musi posiadać stałą długość w czasie, aby obraz na ekranie telewizora był stabilny.

 

Przerwania Wideo (INT i NMI)


Przerwania INT

Przerwania Maskowalne INT są generowane, gdy bit nr 6 rejestru odświeżania R osiągnie wartość 0. Ponieważ rejestr R jest zwiększany raz dla każdego kodu wykonywanej instrukcji (dwa razy dla kodów z prefiksem), to nie istnieje liniowa zależność pomiędzy cyklami zegarowymi a cyklami odświeżania pamięci dynamicznych RAM.

W ZX-81 przerwania INT są używane do kończenia rysowania linii skanujących, dane wyświetlacza są "wykonywane" identycznie jak instrukcje NOP, a za nimi znajduje się kod instrukcji HALT (który jest identyczny w działaniu jak seria powtarzających się instrukcji NOP), więc w tym specjalnym przypadku (skoro zarówno HALT jak i NOP zwiększa R raz na 4 cykle zegarowe), przerwania INT mogą być wykorzystane do wytworzenia regularnego odstępu.

Powyższa kombinacja INT/HALT używana jest w charakterze opóźnienia o zmiennej długości, które jest konieczne tylko dla linii skanujących o zmiennej długości (tj. przy mieszanych liniach skanujących zwiniętych i rozwiniętych). Linie skanujące o stałej długości mogłyby być kończone sprzętowo.

W trybie przerwań IM 1 (standardowym) sterownik INT jest umieszczony pod adresem $0038 w pamięci ROM BIOS. Przerwania INT są włączane za pomocą instrukcji EI i automatycznie wyłączane po wykonaniu (lub przez wykonanie instrukcji DI).

 

Przerwania NMI

Przerwania niemaskowalne NMI są wymagane podczas okresu powrotu poziomego (tj. na końcu każdej linii skanującej), mikroprocesor jest zmuszony do wejścia w stan oczekiwania na okres trwania żądania NMI (o ile nie wykonuje instrukcji HALT, której wolno się zakończyć bez stanów oczekiwania).

Przerwania NMI są wykorzystywane do zliczania wyświetlonych linii skanujących podczas okresów wygaszania. Pozwala to na wykonywanie programu użytkownika w trybie SLOW w trakcie wyświetlania górnego i dolnego marginesu ekranu, a następnie do przekazania z powrotem sterowania do programu obsługi przerwań i wygaszania, gdy sterownik NMI zdecyduje się zakończyć okres wygaszania.

Sterownik przerwań NMI jest umieszczony w pamięci ROM BIOS pod adresem $0066 (niezależnie od trybu przerwań IM). Przerwania NMI są włączane/wyłączane za pomocą operacji we/wy - mikroprocesor nie potrafi wyłączyć przerwań NMI (tj. instrukcje DI/EI nie wpływają na przerwania NMI).


Parametry czasowe wyswietlania obrazu wideo

Linie skanujące

Wyświetlanie poziome 128 cykli (32 znaki, 256 pikseli)
Wygaszanie poziome 64 cykle (lewy i prawy margines)
Powrót poziomy 15 cykli  
Całkowity czas skanowania: 207 cykli  

Częstotliwość oraz czas trwania powrotów poziomych są ustalone. Procedura wyświetlania mogłaby zwiększyć lub zmniejszyć szerokość obszaru ekranu (przez odpowiednie ustawienie czasów wygaszania), chociaż szersze ekrany mogłyby wykraczać poza widoczne rozmiary ekranu współpracującego z komputerem odbiornika telewizyjnego lub monitora.

 

Czasy pionowe - 50 Hz

Górne wygaszanie 11592 cykle 56 linii skanujących (7 wierszy znakowych)
Obszar wyświetlany 39744 cykle 192 linie skanujące (24 wiersze znakowe)
Dolne wygaszanie 11592 cykle 56 linii skanujących (lub nieco mniej)
Powrót pionowy 1235 cykli 6 linii skanujących


 

Czasy pionowe - 60 Hz

Górne wygaszanie 6624 cykle 32 linie skanujące (4 wiersze znakowe)
Obszar wyświetlany 39744 cykle 192 linie skanujące (24 wiersze znakowe)
Dolne wygaszanie 6624 cykle 32 linie skanujące (lub nieco mniej)
Powrót pionowy 1235 cykli 6 linii skanujących


Puste linie skanujące dostępne dla użytkownika

Chociaż górny margines ekranu składa się z 56 linii skanujących (32 w trybie 60Hz), to tylko w czasie 54 z nich (30 dla 60Hz) może być wykonywany program użytkownika. Pierwsza z pozostałych linii skanujących jest zajęta przez instrukcję HALT (którą zwalnia przerwanie NMI, dając dokładną synchronizację powrotu plamki), a następna jest tracona na zwinięty wiersz D_FILE. Dolny margines ekranu jest identyczny jak górny, z wyjątkiem tego, że na spodzie nie jest wyświetlany zwinięty wiersz D_FILE. Zamiast tego część cykli jest używana do zwiększania licznika ramek.

 

Czas wygaszania dostępny dla użytkownika

W każdej linii skanowania nie jest dostępne 207 cykli dla programu użytkownika, nawet w okresach wygaszania. Przerwanie NMI jest generowane w każdej linii, włączając w to kilka taktów oczekiwań NMI, następnie wykonywane jest wywołanie CALL $66 i następuje wykonanie kodu sterownika przerwań NMI, który zlicza linie skanujące wyrysowane na ekranie telewizora.

Całkowity czas linii skanującej 207 cykli
Cykle oczekiwania NMI/CALL $66 23 cykle
Sterownik przerwań NMI 32 cykle
Pozostały czas dla użytkownika 152 cykle

Łącznie w górnym i dolnym marginesie dostępne są dla programu użytkownika 54 × 2 linie skanujące na jedną ramkę obrazu w trybie 50Hz (tylko 30 × 2 w trybie 60Hz).


Wynikowy czas procesora dostępny dla programów użytkownika

Tryb SLOW, 50Hz, prędkość efektywna mikroprocesora 0.8208 MHz (54 × 2 × 152 cykle, 50 ramek)
Tryb SLOW, 60Hz, prędkość efektywna mikroprocesora 0.5472 MHz (30 × 2 × 152 cykle, 60 ramek)
Tryb FAST, całkowita prędkość mikroprocesora 3.2500 MHz (wyświetlanie wyłączone)

 

Zestaw znaków

Poniżej mamy zestaw znaków komputera ZX-81 wraz z ich kodami w systemie szesnastkowym (aby otrzymać kod znaku w numerze wiersza zastąp ostatnie zero numerem kolumny):

 


Znaki mogą być wyświetlane jako normalne (bit nr 7 wyzerowany) lub jako negatywowe (bit nr 7 ustawiony na 1):

 

 

Znaki o kodach $00 ... $07 i $80 ... $87 są stosowane do tworzenia grafiki blokowej zbudowanej z pikseli 4 × 4 punkty. Dodatkowo mamy również znaki $08 ... $0A i $88 ... $8A do tworzenia różnych efektów cieniowania. Dla pikseli blokowych można utworzyć wszystkie 16 kombinacji używając znaków zwykłych oraz negatywowych. Dla znaków cieniowanych dostępne jest tylko 6 różnych kombinacji - znaki cieniowane zwykłe i negatywowe nie łączą się ze sobą zbyt dobrze.

Matryce znaków ZX-81 umieszczone są w pamięci ROM w obszarze od $1E00 ... $1FFF (rejestr I = $1E).

Młodsze 9 bitów (A0-A8) zestawu znaków jest adresowane przez układ wyświetlania obrazu (unieważniając sygnały adresowe dostarczone przez mikroprocesor, który umieszcza na magistrali adresowej całą parę rejestrów IR) - te specjalne bity adresowe są kierowane do obszaru układu pamięci ROM (lecz nie do RAM), zatem pamięć RAM nie może być używana do przechowywania matryc znakowych (bez specjalnych przeróbek).

 

 


   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2018 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe