VBXEFXULA: Biblioteka emulująca mapę atrybutów układu ULA (wykorzystywanego w komputerach Sinclair ZX Spectrum 48k) za pomocą VBXE z rdzeniem FX 1.2x.
I. Organizacja ekranu ZX Spectrum
Ekran rysowany jest na ZX Spectrum w oparciu o dwie struktury ulokowane w pamięci RAM:
pamięć danych ekranu ($4000..$57FF),
pamięć atrybutów ($5800..$5AFF).
Dane obrazu: Pamięć danych ekranu zawiera informację o punktach wyświetlanych na obrazie. Każdy bajt pamięci przechowuje informację o 8 punktach linii ekranowej. Bit o wartości 0 oznacza że punkt będzie miał "kolor papieru" (ang. paper color), bit o wartości 1 - "kolor atramentu" (ang. ink color). Kolejnym punktom odpowiadają bity uszeregowane od najstarszego do najmłodszego.
Ekran podzielony jest na linie zawierające kolejne 32 bajty, co odpowiada 256 kolejnym punktom linii.
Całkowity obszar ekranu liczący 192 linie podzielony jest na 3 bloki (po 64 linie każdy), które położone są w pamięci jeden po drugim.
Każdy blok z kolei podzielony jest na logiczne wiersze ekranowe złożone z 8 linii. Adresy danych dla kolejnych linii w obrębie bloku przyporządkowane są następująco:
Obszar danych pamięci ekranu zajmuje łącznie $1800 bajtów pamięci i jest ulokowany począwszy od adresu $4000 aż do $57FF. Adres danej na ekranie:
data address = %01CCAAABBBXXXXX
gdzie: X - numer bajtu w linii powstały z podzielenia współrzędnej x piksela przez 8 ABC - numer linii ekranu %CCBBBAAA
Atrybuty:
Pamięć atrybutów zawiera dane o kolorach używanych przez fragmenty ekranu o rozmiarze 8x8 pikseli.
Inaczej niż pamięć ekranu jest ona zorganizowana w sposób liniowy, tak więc atrybuty kolejnych wierszy ekranu (złożonych z 8 linii każdy) następują bezpośrednio po sobie, każdy wiersz natomiast zawiera atrybuty kolejnych obszarów 8x8 pikseli począwszy od lewej strony ekranu do prawej.
Adres danej atrybutu:
attribute addres = %010110CCBBBXXXXX
gdzie: X - numer atrybutu w wierszu (powstały z podzielenia współrzędnej x piksela przez 8) BC - numer wiersza ekranu %CCBBB (powstały z podzielenia numeru linii, czyli współrzędnej y punktu, przez 8)
Pamięć atrybutów zajmuje obszar $300 bajtów i jest ulokowana począwszy od adresu $5800 aż do $5AFF.
Kolory:
Poszczególne bity w bajcie atrybutu %FBPPPIII odpowiadają za:
III - kolor atramentu (ang. ink),
PPP - kolor papieru (ang. paper),
B - rozjaśnienie (ang. bright),
F - migotanie (ang. flash).
Każdy punkt na ekranie może przybierać dowolny kolor z palety 8 barw definiowanych za pomocą składowych RGB:
%000 - BLACK #000000 %001 - BLUE #0000CD %010 - RED #CD0000 %011 - MAGENTA #CD00CD %100 - GREEN #00CD00 %101 - CYAN #00CDCD %110 - YELLOW #CDCD00 %111 - WHITE #CDCDCD
Kolor punktu o "kolorze atramentu" (bit w pamięci ekranu o wartości 1) określa składnik III w odpowiadającym mu atrybucie, "kolor papieru" (bit o wartości 0) określa składnik PPP atrybutu.
Kiedy atrybut zawiera ustawiony składnik F, wtedy składniki PPP i III zamieniane są z sobą z półsekundowym interwałem, co powoduje migotanie punktów w obszarze całego atrybutu.
Przykładowo mając zdefiniowany fragment ekranu o rozmiarze 8x8:
i atrybut o wartości:
%10000111
ustalający czarny kolor papieru i biały kolor atramentu spowoduje, że ULA wyświetli czarno-białą szachownicę, która cyklicznie będzie zamieniać miejscami barwy punktów:
Składnik B atrybutu powoduje rozjaśnienie wszystkich kolorów w obrębie atrybutu. Wybierane są one wtedy z jaśniejszej palety:
%000 - BLACK #000000 %001 - BLUE #0000FF %010 - RED #FF0000 %011 - MAGENTA #FF00FF %100 - GREEN #00FF00 %101 - CYAN #00FFFF %110 - YELLOW #FFFF00 %111 - WHITE #FFFFFF
Kolor ramki ekranu może być wybrany jedynie z palety podstawowej za pomocą zapisania pierwszych 3 bitów rejestru I/O o adresie $xxFE.
I. Emulacja układu ULA za pomocą VBXE
Biblioteka przeprowadza emulację mapy atrybutów układu ULA używając tylko mapy atrybutów oraz funkcji blittera rdzenia FX karty VBXE.
Przy projektowaniu biblioteki poczyniono pewne założenia:
1. Za rysowanie treści obrazu oraz jego organizację w pamięci komputera odpowiada wyłącznie ANTIC, natomiast funkcje nakładania kolorów przejmuje VBXE. 3. Atrybuty ekranu ULA znajdują się w stałym miejscu pamięci w obszarze $5800..$5AFF. 4. Obszar emulacji atrybutów rozpoczyna się od 32 linii ekranu (zakładając, że ANTIC kreśli obraz począwszy od linii 8) i rozciąga się na wąskim ekranie przez kolejne 192 linie. 2. Emulacja nie zajmuje czasu CPU, ponieważ wszystkie operacje realizowane są za pomocą blittera rdzenia FX. 4. Tryb emulacji można dowolnie włączać lub wyłączać wedle upodobania. 5. Ilość pamięci zajmowanej przez bibliotekę a niezbędnej do poprawnej emulacji powinna być jak najmniejsza.
W wersji minimalnej po inicjalizacji biblioteki użytkownik powinien tylko co pewien czas (np. w przerwaniu VBLK) wywoływać funkcję uruchamiającą blitter, który powoduje odświeżenie zawartości mapy atrybutów VBXE na podstawie atrybutów ULA.
Dodatkowo biblioteka umożliwia:
1. Ustawianie koloru ramki. 2. Dowolne włączanie i wyłączanie trybu emulacji. 3. Przeliczanie wartości kolorów ULA na wartości kolorów GTIA (kiedy tryb emulacji jest wyłączony) z uwzględnieniem używanego systemu TV (PAL/NTSC).
Dodatkowa funkcjonalność może zostać usunięta (jeśli jest zbędna) bez szkody dla samej emulacji.
Sama biblioteka nie używa żadnych przerwań pozostawiając ich wykorzystanie w gestii programisty.
Atrybuty:
Podczas generowania obrazu para ANTIC+GTIA pobiera wartości kolorów dla punktów odpowiednio z rejestrów COLPF1 (punkt zapalony - 1) i COLPF2 (punkt zgaszony - 0). W efekcie na całym ekranie monitora otrzymujemy dwubarwny obraz.
Mapa atrybutów FX pozwala podzielić generowany przez parę ANTIC+VBXE obraz na mniejsze fragmenty (w naszym przypadku 8x8 punktów) i przedefiniować wartości kolorów dla punktów w każdym fragmencie z osobna.
Mapa atrybutów FX składa się z 4-bajtowych elementów:
$00: PF0 - indeks koloru dla rejestru COLPF0 $01: PF1 - indeks koloru dla rejestru COLPF1 $02: PF2 - indeks koloru dla rejestru COLPF2 $03: PAL - numer palety używanej przez kolory GTIA
Każdy z takich elementów (zwany atrybutem) pozwala na redefinicję kolorów używanych do generowania punktów obrazu w obszarze 8x8. Ponieważ w trybie hi-res używane są wyłącznie kolory COLPF1 i COLPF2, blitter na podstawie atrybutów ULA ustawia wyłącznie pola PF1 i PF2 atrybutu indeksami kolorów ULA.
Atrybuty zorganizowane są w matrycę 32x24 o rozmiarze 8x8 pikseli hi-res i pokrywają obszar wąskiego ekranu ANTIC-a (256 punktów w linii) począwszy od 32 linii ekranowej (pierwsza linia display list rysowana jest w 8 linii ekranu) przez 192 kolejne linie.
Mapa atrybutów FX zajmuje w pamięci VBXE obszar $00000..$00BFF.
Kolory:
Wszystkie kolory GTIA (czyli COLPMx, COLPFx oraz COLBAK) wybierane są z palety VBXE o numerze 3. Paleta ta zawiera definicje wszystkich kolorów ULA w indeksach:
%.BPPPIII
gdzie: B - rozjaśnienie PPP - kolor papieru III - kolor atramentu
Emulacja używa wyłącznie 30 kolorów o indeksach:
- %0x000xxx - dla atrybutów "koloru atramentu" - %0xxxx000 - dla atrybutów "koloru papieru"
Pozostałe kolory w palecie mogą być użyte do innych celów (np. do kolorowania PMG czy ramki).
Flash:
Ustawienie składowej F atrybutu ULA powoduje okresową zamianę kolorów atramentu z kolorem papieru skutkując naprzemiennym migotaniem punktów w obszarze atrybutu. Program blittera zlicza ilość wywołań procedury odświeżającej i cyklicznie powoduje zamianę wartości rejestrów kolorów PF1 i PF2 w odpowiednim atrybucie FX.
Mając więc początkowo:
$00: PF0 $01: PF1 - %00000000 - indeks koloru czarnego w palecie VBXE $02: PF2 - %00111000 - indeks koloru białego w palecie VBXE $03: PAL
po upływie pół sekundy zawartość atrybutu FX zmieni się na:
by po upływie kolejnej połowy sekundy powrócić do stanu początkowego.
Funkcjonalność ta realizowana jest przez bibliotekę całkowicie za pomocą programu blittera, a interwał zmian wyznaczony jest na 16 wywołań programu blittera (jeśli odbywa się to co ramkę, wtedy co 16 ramek).
Blitter:
Jak już wspomniano za pomocą blittera realizowane są dwa zadania:
konwersja atrybutów ULA na atrybuty FX,
zamiana kolorów atrybutu FX przy ustawionej składowej F atrybutu ULA.
Zanim nastąpi opis ich realizacji, warto przedstawić garść informacji pozwalających poznać specyfikę blittera VBXE i jego możliwości.
Każdy program blittera złożony jest z co najmniej jednego bloku BCB. Struktura BCB opisuje zadanie przesłania danych z jednego obszaru pamięci VRAM do innego, oraz operacje które podczas każdego przesłania zostaną wykonane po pobraniu bajtu z obszaru źródłowego i przy zapisie do obszaru docelowego.
srcdx - odstęp między bajtami linii bloku źródłowego
srcdy - odstęp między liniami bloku źródłowego
dstad - adres docelowy
dstdx - odstęp między bajtami linii bloku docelowego
dstdy - odstęp między liniami bloku docelowego
width - szerokość przesyłanego bloku danych
height - wysokość przesyłanego bloku danych
and - maska dla operacji AND na bajcie źródłowym
xor - wartość dla operacji XOR na bajcie źródłowym
mode - tryb pracy blittera oraz znacznik końca listy BCB
Pozostałe parametry bloku BCB nie są istotne z punktu widzenia operacji biblioteki, więc ich opis zostanie pominięty - można go zresztą znaleźć w podręczniku programisty VBXE.
Kolejne bloki BCB w liście ułożone są jeden za drugim i wykonywane przez blitter sekwencyjnie począwszy od bloku wskazanego przez rejestr BLADR ($D650) aż do bloku oznaczonego, jako ostatni.
Ponieważ blitter został zaprojektowany z myślą o kopiowaniu obszarów dwuwymiarowych na ekranie graficznym, dlatego przesłania odbywają się na blokach o podanej szerokości oraz wysokości (rozłożonych jednakże w jednowymiarowej pamięci VRAM). Przetwarzanie danych odbywa się linia po linii aż do przetworzenia wszystkich danych źródłowych - ich ilość określa wzór:
operation count = width * height
Po pobraniu danej blitter wykonuje na niej kolejno dwie binarne operacje maskujące:
1. AND ze stałą and, 2. XOR ze stałą xor.
Teraz na obliczonej wartości w miejscu docelowym wykonywana jest jeszcze operacja określona w polu mode:
0 - COPY - zapis, 1 - OVER - zapis tylko jeśli wartość niezerowa, 2 - ADD - suma arytmetyczna i zapis, 3 - OR - binany OR i zapis, 4 - AND - binarny AND i zapis, 5 - XOR - binarny XOR i zapis.
Następnie blitter oblicza kolejne adresy danej źródłowej i docelowej po czym powtarza wszystkie kroki aż do przetworzenia wszystkich danych.
UWAGA! W trybie OVER blitter zapisuje w miejscu docelowym wyliczoną wartość _tylko_wtedy_ kiedy jest ona różna od 0 - w przeciwnym wypadku wartość zostaje utracona a bajt w miejscu docelowym nie ulega zmianie!
Zamiana kolorów:
Mruganie kolorów w obrębie atrybutu odbywa się z interwałem co 16 wywołań programu blittera. Musi on więc za każdym razem kiedy podejmuje pracę zwiększać licznik FX_FLASHCNTR po czym na podstawie jego wartości ustalić znacznik odwracania kolorów w atrybutach ULA, które mają ustawiony komponent F.
UWAGA! Dla zwiększenia czytelności opisu w blokach BCB pominięto pola nie wpływające na realizowane zadania.
Samo zadanie zwiększania licznika odbywa się za pomocą następującego BCB:
Pole and ma wartość 0, dzięki czemu dowolna dana źródłowa po wykonaniu AND da w wyniku 0. Blitter optymalizuje swoją pracę i w tym wypadku nawet nie pobiera danej z pamięci, lecz bierze wartość bezpośrednio z pola xor (0 XOR V daje w wyniku V). Taki blok zawsze więc realizuje operację określoną polem mode ze stałą podaną w polu xor. Stąd też nie ma znaczenia zawartość pól srcad, srcdx oraz srcdy. Stała wartość (8) dodawana (ADD) jest do bajtu pod adresem FX_FLASHCNTR i tamże zapisywana. Po przekroczeniu wartości 255 następuje przepełnienie licznika FX_FLASHCNTR i zaczyna on znowu zliczać w górę od 0. Ten BCB powoduje przetwarzanie tylko jednego bajtu danych na co wskazują pola width i height (rzeczywiste rozmiary muszą być w BCB zmniejszane o 1).
Zawartość FX_FLASHCNTR jest maskowana z wartością %10000000 i zapisywana w FX_FLASHFLAG. Bit 7 FX_FLASHCNTR zmienia swój stan co 16 wywołań programu blittera ponieważ: 1. Licznik zwiększany jest o 8 co każde wywołanie. 2. Przepełnienie licznika następuje co 256 / 8 = 32 wywołania. 3. Zmiana wartości bitu 7 występuje dokładnie w połowie, czyli po 32 / 2 = 16 wywołaniach blittera. Modyfikując stałą inkrementacji licznika FX_FLASHCNTR można wpłynąć zatem na interwał, z którym zmieniać się będzie flaga FX_FLASHFLAG.
Konwersja atrybutów:
Zadanie konwersji atrybutów jest nieco bardziej złożone głównie ze względu na konieczność uwzględnienia zamiany kolorów w atrybutach.
Sama idea obliczania atrybutów FX na podstawie atrybutów ULA zasadza się na określeniu indeksu koloru w palecie FX osobno dla "koloru atramentu" i osobno dla "koloru papieru". Ponieważ za kolory odpowiednich pikseli odpowiadają rejestry COLPF1 oraz COLPF2, to w atrybucie FX należy po prostu ustawić indeks koloru bazując na składowych III oraz PPP atrybutu ULA (z dodatkiem składowej B).
Jak poprzednio wspomniano w palecie FX zdefiniowano jedynie 30 kolorów. Indeksy tych kolorów w palecie odpowiadają zamaskowanym składowym:
"koloru atramentu": %01000111
a więc indeksom $00..$07 oraz $40..$47,
i "koloru papieru": %01111000
czyli indeksom:
$00, $08, $10, $18, $20, $28, $30 i $38, oraz
$40, $48, $50, $58, $60, $68, $70 i $78.
Tak więc kompletne zadanie konwersji atrybutów potrafiłyby wykonać dwa bloki BCB zdefiniowane tak:
Sprawę jednak komplikuje składowa F atrybutu ULA ponieważ zależnie od jej stanu do odpowiednich pól atrybutu FX odpowiadających za kolory COLPF1 i COLPF2 musi raz trafić odpowiednio "kolor atramentu" oraz "kolor papieru", innym razem zaś odwrotnie - "kolor papieru" oraz "kolor atramentu". Ponadto zamiana odbywa się dynamicznie - przez 16 wywołań programu blittera kolory nie mogą być odwracane nawet jeśli w atrybucie ULA składowa F jest ustawiona, a przez kolejne 16 wywołań muszą zostać odwrócone tylko dla atrybutów z ustawionym F (dla pozostałych muszą pozostać nieodwrócone).
Przyda się więc teraz znacznik FX_FLASHFLAG.
Zadanie polega na obliczeniu dwóch obszarów maskujących odpowiednie bity atrybutu ULA zależnie od stanu składowej F atrybutu ULA i wartości znacznika FX_FLASHFLAG.
Na początek ustalmy wartości masek dla "koloru atramentu" tak, jak gdyby na całym ekranie nie była używana składowa F:
Powód dla którego maska ustalona jest na %10111111 (czyli tak, jakby miały zostać przeniesione obydwie składowe III oraz PPP, a co gorsza F!) zostanie wyjaśniony za moment. Następnie należy określić, które atrybuty ULA mają ustawioną składową F.
Składowa F znajduje się na ostatnim bicie atrybutu ULA, więc and ma wartość %10000000. Blitter zamaskuje składową F i skopiuje wynik do pola COLPF1 atrybutu FX.
UWAGA! Tryb OVER, jak wspomniano wcześniej, zapisze daną w miejscu docelowym _wyłącznie_wtedy_ gdy ma ona wartość niezerową!
Tak więc w atrybutach FX zostaną zmodyfikowane tylko pola, które mają w odpowiednich atrybutach ULA _ustawiony_bit_F_! Cała reszta pozostanie bez zmian. W efekcie pola COLPF1 atrybutów FX będą miały tylko dwie możliwe wartości:
%10111111 gdy F=0
%10000000 gdy F=1
Ponieważ w polu COLPF1 chcemy mieć:
%01000111, czyli "kolor atramentu" wtedy kiedy kolory mają być nieodwrócone,
%01111000, czyli "kolor papieru" kiedy kolory muszą być odwrócone
to aby otrzymać żądany wynik wystarczy na wartościach pola COLPF1 wykonać operację XOR z wartością %11111000, co też robi właśnie następny blok BCB:
Skoro w polu COLPF1 mamy maskę dla odpowiedniego "koloru" atrybutu ULA, to w COLPF2 powinna znaleźć się maska dla "koloru" przeciwnego. Należy więc tylko odwrócić bity maskujące składniki i zapisać je w polach COLPF2:
Maski mamy zatem przygotowane - pozostało przenieść wartości atrybutów ULA do pól COLPF1 i COLPF2 i zamaskować je wyliczonymi wartościami dla "kolorów" ULA.
Warto jeszcze wyjaśnić gdzie znajduje się w pamięci znacznk FX_FLASHFLAG. Kiedy przyjrzeć się bliżej blokowi BCB odpowiadającemu za przenoszenie składników F atrybutów ULA do pól COLPF1 atrybutów FX:
można dojrzeć, że znacznik FX_FLASHFLAG został umieszczony w polu and bloku BCB. Mamy więc do czynienia z kodem samomodyfikowalnym! Taka sztuczka pozwala za pomocą jednego bloku BCB przenieść wszystkie atrybuty ULA z ustawionym F pod równoczesnym warunkiem, że znacznik FX_FLASHFLAG _też_każe_odwracać_ kolory w atrybucie (FX_FLASHFLAG=%10000000 oraz zamaskowany F również ma wartość %10000000)!
Ten blok dodaje stałą z pola xor do wyniku, więc nie ma znaczenia zawartość pól srcad, srcdx i srcdy, a skoro tak to pierwszy bajt srcad wykorzystany został właśnie do przechowania wartości licznika.
Pamięć:
Biblioteka rezerwuje do swoich potrzeb pamięć VBXE w obszarach:
$00000..$00BFF - atrybuty FX $00C00..$00C0E - XDL $00C0F..$00CB6 - program blittera
Te obszary są wykorzystywane wyłącznie przez VBXE i w trakcie działania emulacji nie są nigdy widoczne w pamięci Atari.
Zupełnie inaczej ma się rzecz z obszarem:
$05800..$05AFF - atrybuty ULA
Jest on włączony na stałe w obszarze $4000..$7FFF pamięci Atari poprzez okno MEMACB VBXE jako bank 1 ($04000..$07FFF) i jest widoczny dla CPU oraz ANTIC-a dzięki czemu użytkownik może wykorzystywać tę pamięć do dowolnych celów. Atrybuty ULA w pamięci Atari widoczne są również w obszarze $5800..$5AFF.
UWAGA! Wyłączenie emulacji nie powoduje odłączenia okna MEMACB, gdyż mogłoby to skutkować zniknięciem danych zapisanych przez użytkownika i pojawieniem się w tym miejscu zwykłej pamięci RAM Atari.
Biblioteka wykorzystuje banki 0 i 1 do własnych celów, aby niezależnie od użytego wariantu rdzenia FX (wersje A lub R) nie ryzykować kolizji z emulowanym rozszerzeniem RAMBO (i potencjalnego zniszczenia hipotetycznego ramdysk-u).
Interfejs: Biblioteka wyposażona jest w zestaw podstawowych procedur umożliwiających jej konfigurację i sprawne działanie.
Inicjalizacja biblioteki:
Po załadowaniu biblioteki do pamięci należy przeprowadzić jej inicjalizację za pomocą bezparametrowej funkcji:
ula_init
Powoduje ona wykonanie następujących działań: 1. Ustalana jest mapa kolorów GTIA zależnie od systemu TV (PAL/NTSC). 2. Przeprowadzana jest detekcja VBXE z rdzeniem FX. 3. Tworzona jest paleta kolorów VBXE mapująca kolory ULA. 4. Tworzona jest mapa atrybutów VBXE o rozmiarze 32x24 pola 8x8 pikseli. 5. Tworzona jest XDL podkładająca atrybuty na obszarze ekranu o rozmiarze 256x192 piksele hi-res. 6. Tworzony jest program blittera w pamięci VBXE. 7. Włączany jest bank 1 VBXE w obszarze $4000..$7FFF widoczny przez CPU i ANTIC. 8. Inicjalizowany jest program blittera i XDL oraz włączane wyświetlanie obrazu przez VBXE.
Po powrocie znacznik Z informuje o pomyślnym wykonaniu zadania (Z=0), co oznacza że system emulacji jest gotowy do działania. W przeciwnym wypadku (Z=1) nie wykryto VBXE lub rdzenia FX.
Na stronie zerowej znajdują się dwa rejestry:
vbxefx - znacznik włączenia systemu emulacji ULA ($FF-włączony, $00-wyłączony),
vbxead - adres basowy rejestrów VBXE ($0000 - brak VBXE lub rdzenia FX).
UWAGA! Obydwa rejestry są niezbędne do działania procedur biblioteki.
Po wykonaniu kod inicjalizacyjny nie jest już potrzebny i można go z powodzeniem usunąć z pamięci Atari.
Aktualizacja atrybutów:
Najważniejszą funkcją biblioteki jest:
ula_refresh
Uruchamia blitter VBXE realizujący odświeżanie zawartości atrybutów FX na podstawie atrybutów ULA. Procedura ta powinna być wywoływana okresowo n.p. na przerwaniu VBLK.
Jeżeli tryb emulacji jest włączony przez cały czas działania programu, to jest to jedyna funkcja, która jest niezbędna do funkcjonowania emulacji. Pozostałe mogą zostać usunięte z pamięci RAM.
Włączenie/wyłączenie emulacji:
W przypadku kiedy zachodzi potrzeba dezaktywacji emulacji ULA należy wywołać procedurę:
ula_off
Wyłącza ona ekran VBXE oraz ustawia flagę vbxefx, która automatycznie blokuje uruchamianie blittera przy wywoływaniu ula_refresh.
UWAGA! Funkcja nie odłącza okna MEMAC, gdyż groziłoby to zniknięciem danych użytkownika (a być może programu obsługi jakiegoś przerwania).
Aktywacja emulacji odbywa się poprzez wywołanie:
ula_on
która włącza ekran VBXE i odblokowuje uruchamianie blittera przy wywołaniach ula_refresh.
Manipulacja kolorem
Rejestry COLPMx, COLPFx i COLBAK układu GTIA przechowują informacje o kolorze. Kiedy emulacja ULA jest wyłączona znajdują się tam informacje o kolorze GTIA:
%CCCCLLLL
gdzie:
CCCC - barwa
LLLL - jasność (w trybie hi-res najmłodszy bit jest ignorowany)
Wartości te są zależne od używanego systemu TV (PAL/NTSC).
PAL
NTSC
Podczas włączonej emulacji, kiedy VBXE zajmuje się generowaniem koloru w tych rejestrach znajduje się indeks koloru z palety 3 odzwierciedlającej wartości kolorów w atrybutach ULA:
%.BPPPIII
gdzie, jak opisano wcześniej, wykorzystywane są jedynie kombinacje:
%0x000xxx
%0xxxx000
Aby uniknąć niewłaściwej interpretacji kolorów przez GTIA i VBXE należy po każdym przełączeniu trybu emulacji ULA przeliczyć wartości kolorów w rejestrach GTIA za pomocą funkcji:
ula_color
przyjmującej w A numer koloru ($0..$F), a zwracającej też w A wartość, którą należy umieścić w rejestrze koloru.
UWAGA! Ze względu na rozbieżność kolorów w paletach FX i GTIA ustawienie kolorów ULA w rejestrach GTIA (COLPMx, COLPFx i COLBAK) spowoduje wyświetlanie innych barw na wyjściu RGB (VBXE) a innych na wyjściu monitorowym oraz telewizyjnym Atari (GTIA).
Ostatnia funkcja biblioteki służy do ustawiania koloru ramki:
ula_border
i przyjmuje w A numer koloru do ustawienia ($0..$F) nie zwracając żadnego wyniku. Powoduje ona ustawienie wartości koloru w rejestrach COLBAKS ($02C8) i COLBAK ($D01A).
Włączanie biblioteki do własnych programów:
Archiwum z biblioteką zawiera pliki:
hardware.icl - deklaracje rejestrów systemowych używanych przez bibliotekę
vbxefxula.icl - stałe deklarowane przez bibliotekę
vbxefxula.asx - kod źródłowy biblioteki
fxulademo.asx - kod źródłowy przykładu wykorzystującego bibliotekę
lookcat.scr - przykładowy obrazek używany przez fxulademo
fxulademo.xex - kod wynikowy przykładu
vbxefxula.txt - opis biblioteki
Źródło biblioteki zostało przygotowane do assemblacji za pomocą MADS-a 2.0.3 dostępnego na stronie http://mads.atari8.info/
Aby włączyć bibliotekę do własnego programu należy wykonać kilka kroków:
1. Włączyć pliki deklaracji:
icl "hardware.icl" icl "vbxefxula.icl"
2. Zdefiniować adresy bazowe i włączyć źródła biblioteki:
A8_ATR to stała zdefiniowana w pliku vbxefxula.icl i określa adres pamięci atrybutów w RAM-ie Atari zgodnie z jej położeniem w ZX Spectrum 48k.
Program przykładowy
Program przykładowy prezentuje rysunek lookcat.scr przeniesiony z ZX Spectrum 48k, którego autorem jest Yerzmyey/H-PRG.
Pod obrazkiem znajduje się linia informacyjna zawierająca nazwę programu oraz ilość linii skanningowych, które zostały narysowane w trakcie wykonania programu blittera (pomiar odbywa się kiedy jest włączony tryb emulacji, ponieważ tylko wtedy blitter pracuje).
Kod demonstruje użycie: - okresowego przełączania trybu emulacji, - efektu mrugania atrybutów, - cyklicznej zmiany koloru ramki w obydwu trybach, - pomiaru czasu pracy blittera (poprzez wykorzystanie przerwań DLI, BLIT oraz nie podłączonego przetwornika POT7 do zliczania linii skanningowych w celu osiągnięcia większej dokładności pomiaru niż przy użyciu VCOUNT).
Czas realizacji programu blittera obrazowany jest dwoma metodami:
1. Wysokością białego paska nad rysunkiem. 2. Liczbą wyświetlaną w postaci szesnastkowej w prawej części linii informacyjnej pod rysunkiem.
Blitter uruchamiany jest na przerwaniu DLI, a moment ten sygnalizowany jest zmianą koloru ramki na biały. Realizuje to następujący fragment kodu:
; dlist interrupt routine
dliint: bit vbxefx bpl ?ret
pha tya pha
; start ULA refresh and scanlines measurment
lda #%00000000 sta SKCTL jsr ula_refresh lda #%00000011 sta SKCTL sta POTGO
; change of background color to indicate start of blitter work
lda #ULA_WHITE jsr ula_color sta COLBAK
pla tay pla
?ret rti
Za pomocą zapisu do rejestru POTGO ($D20B) uruchamiany jest również pomiar czasu, jaki zajmie realizacja programu blittera.
Po zakończeniu programu blitter generuje przerwanie IRQ obsługiwane za pomocą kodu:
; restore background color to indicate end of blitter work
lda COLBAKS sta COLBAK
; print number of scanlines passed during blitter work
lda POT7 ldy #29 jsr prnhex
pla tay pla rti
?skip2 pla tay pla ?skip1 jmp (store.?vimirq)
Następuje wtedy odczytanie licznika POT7 ($D207), który zlicza ilość linii ekranowych od momentu jego uruchomienia (odbywającego się zapisem do POTGO) i zasygnalizowanie zakończenia pracy blittera przywróceniem koloru ramki.
Kilku słów wyjaśnienia może wymagać metoda użyta do pomiaru czasu wykonania programu blittera.
Obliczenie czasochłonności procesów zazwyczaj odbywa się z użyciem rejestru VCOUNT ($D40B) udostępniającego informację o numerze aktualnie rysowanej przez ANTIC linii ekranowej. Niestety wadą tego rozwiązania jest to, że VCOUNT zwiększany jest co drugą linię ekranową, więc nie bardzo nadaje się do pomiaru krótkotrwałych procesów. Licznik POT7 jest w rzeczywistości rejestrem przetwornika ADC, który zlicza czas ładowania kondensatora aż do osiągnięcia wartości napięcia referencyjnego na jego wejściu. Takich przetworników jest w Atari 8, a ponieważ akurat przetworniki POT4..POT7 nie są w serii XL/XE nigdzie podłączone, odpowiadające im liczniki liczą w nieskończoność (dokładnie od 0 do 228) z okresem pojedynczej linii skanningowej (przy wykorzystaniu tzw. "trybu wolnego"). Uruchamiając więc przy starcie blittera licznik POT7 zapisem do POTGO i czytając jego wartość po zakończeniu pracy blittera uzyskujemy pomiar dokładniejszy niż w przypadku wykorzystania rejestru VCOUNT (przy założeniu, że wykonanie programu blittera nie potrwa dłużej niż 228 linii skanningowych).
Assemblacja programu przykładowego za pomocą MADS-a:
$ mads -o:fxulademo.xex fxulademo.asx
Gorąco zachęcam do analizy kodu źródłowego biblioteki, oraz programu przykładowego. W razie niejasności, jakichkolwiek pytań lub znalezionych błędów proszę o kontakt pocztą elektroniczną na adres mono@i-demo.pl
Wady:
Jako, że nie istnieje program doskonały należy na koniec wspomnieć o wadach przedstawionego rozwiązania wynikających z przyjętych założeń:
1. Ponieważ mapa atrybutów przetwarzana jest w całości, co zajmuje czas równoważny rysowaniu około 12 linii ekranowych, biblioteka _nie_nadaje_się_ do wykorzystania kiedy program modyfikuje atrybuty ULA w trakcie rysowania obrazu (programowe tryby graficzne pozwalające uzyskać inne kolory atrybutu w każdej linii ekranowej). 2. Różnice w paletach kolorów VBXE i GTIA prowadzą do wyświetlania różnych obrazów przez wyjście monitorowe i telewizyjne (GTIA) oraz wyjście RGB (VBXE). 3. Obszar atrybutów ULA zmapowany jest w stałym miejscu pamięci Atari, co może utrudniać nieco organizację danych i programu.
Wady te wynikają z przyjętych uproszczeń i powinny zostać zniwelowane w przyszłych wersjach biblioteki.
Może warto zniwelować różnice wyświetlania kolorów pomiędzy VBXE i GTIA (stosowna paleta została jakiś czas temu wysłana do electrona)
mono @2016-07-14 11:21:37
Różnice o których mowa przy wyliczaniu wad nie wynikają z emulacji GTIA, bo przecież w palecie #3 definiuję kolory ZX Spectrum. Różnica w wyświetlaniu kolorów polega na tym, że paleta ZX Spectrum zdefiniowana jest w 30 indeksach palety w miejscach, którym odpowiadają zupełnie inne kolory GTIA. Inny obraz na wyjściach RGB i monitorowym/telewizyjnym otrzymamy kiedy w rejestrach COLBAK, COLPMx lub COLPFx ustawimy indeks koloru z palety ZX Spectrum. Przykładowo chcąc ustawić kolor czerwony w COLBAK, czyli kolor o indeksie indeks 2 w palecie ZX, GTIA na wyjściu monitorowym będzie wyświetlać ciemnoszary, natomiast VBXE pokaże kolor czerwony. Da się to poprawić stosując mapowanie kolorów ZX Spectrum i przedefiniowywując w palecie kolory najbardziej zbliżone do odpowiednich barw w palecie ZX Spectrum - czyli dla rzeczonego czerwonego należałoby używać indeksu $22 dla PAL lub $42 dla NTSC - wtedy GTIA na wyjściu monitorowym pokaże kolor jak najbardziej zbliżony do czerwonego z palety ZX Spectrum, natomiast VBXE pokaże czerwony (bo taki w tym miejscu byłby zdefiniowany).
mgr_inz_rafal @2016-07-14 11:33:17
Niesamowite... Mono cały czas w dobrej formie!
the fender @2016-07-14 12:06:23
Solidna dawka wiedzy fachowej.
_rocky @2016-07-14 12:29:01
A ciekawe, czy dałoby się zrealizować możliwości C64.. W sumie mamy Rapidusa z pamiecią liniową i tam byłby cały kod programu, a po stronie VBXE dane graficzne oraz obsługa spritów.. Taki emulator C64 dałby dostęp do ogromnej bazy gier bez potrzeby portowani :)
the fender @2016-07-14 12:30:35
@_rocky: a nie taniej nabyć po prostu C=64? ;)
mono @2016-07-14 12:43:32
Emulacja VIC-a jest w planie, ale to trudniejsze zadanie. ULA to zaledwie wprawka :) choć powstała z myślą o osobach portujących gry z ZX Spectrum, bo włączenie i inicjalizacja biblioteki a potem użycie jednej funkcji na przerwaniu VBLK (uruchomienie blittera zajmuje 10 cykli CPU) daje mapę atrybutów kiedy mamy w komputerze zainstalowane VBXE. W optymalnej postaci (kiedy używamy tylko uruchomienia blittera i nie przełączamy emulacji ULA w te i we wte) cała biblioteka zajmuje w pamięci raptem 6 bajtów.
larek @2016-07-14 13:48:45
Proste i gotowe :-) READY []
Filtr Smaka coraz bliżej :)
Adam @2016-07-14 20:26:25
Dzięki, Mono, za artek!
mono @2016-07-14 21:00:42
Nie ma za co. Dziękuję Panowie za komentarze :)
W międzyczasie okazało się, że flash jest dość jasno zdefiniowany i powinien odbywać się co 32 ramki (a nie jak liczyłem co 42). W archiwum jest już wersja poprawiona (dokumentacja również).
Mono? A kiedyż to ukończysz swoją strzelankę kosmiczną w trybach GTIA? Posiadaczy VBXE jest tyle... co kot napłakał więc po co niepotrzebnie się męczysz??? :O
mono @2016-07-15 15:24:30
Bo koronnym argumentem przeciw posiadaniu VBXE jest zawsze "ale bo nie ma na to narzędzi". Są narzędzia, będą posiadacze :) A strzelanek jest na Atari mnóstwo ;] Zrobię, zrobię, ale nie wiem kiedy bo ciekawych tematów jest tyle, że jednego życia nie starczy :)
Mono - rób na vbxe, bo zwykłe strzelanki są w nadmiarze ;)
xeen @2016-07-15 22:25:59
być może inni tego nie potwierdzą, ale dokumentacja do VBXE jest dla mnie tak sucha jak... no nie napiszę tego, że kilka już razy mnie odrzuciła. Nazwiecie mnie leniem i może słusznie, ale trochę szkoda, że nie ma tutoriali z przykładami, może to jakoś by wpłynęło na popularyzację bo jest przecież wsparcie w emu.
Dobra robota, Mono!
mono @2016-07-15 22:50:58
@fan mono: Flaszki to ja nie, bo nie lubię, ale na piwo przy stosownej okoliczności to czemu nie, ale muszę znać personalia osoby :) Z obcymi nie piję hahaha. @pin: Ty pokazuj muzę co zrobiłeś, a nie wyłączasz telefon. @xeen: Może tutorial jest suchszy niż dokumentacja :)
xeen @2016-07-15 23:10:12
niż ta dokumentacja? nie ma takiej opcji :)
fan mono @2016-07-16 11:10:22
to się szykuj na kratkę do obalenia :)
ps. a jak się słuchało 'a cow prank' to się po ścianach zjeżdżało ;)
tebe @2016-07-20 20:59:56
a kiedy player MOD-ów ze wsparciem VBXE :), VBXE liczy próbkę sampla wg zadanej częstotliwości, 6502 zapisuje do rejestru POKEY-a LDA/STA
Pokeya - to kiszka, ale na Covox'ie brzmi to niezgorzej (rdzeń SB i kabelek). Niemniej jednak player do modów wymaga znacznych poprawek.
mono @2016-07-21 11:03:17
@pin: Ty mówisz o SoundBoard (choć uruchomionym na VBXE), a tebe mówi o przygotowywaniu sampla granego procesorem na POKEY-u przez blitter VBXE (tego samego można by spróbować z Weroniką). @tebe: Jak parę postów wcześniej napisałem - jednego życia nie starczy...
tdc @2016-07-21 16:46:21
Nikt mi już nie powie, że mam najdłuższe artykuły :P
Wielki szacun Mono!!;)
mono @2020-10-28 12:47:54
Jako że upłynęły już eony od publikacji artykułu, a treści zawarte w internecie nie zostają tam na wieki (prócz zawartości AtariOnline ;]), czas aby zaktualizować wygasłe linki: 1. Podręcznik programisty VBXE: https : //lotharek.pl/files/vbxe/docs.zip 2. Przewodnik po ZX Spectrum: http : //speccy.pl/wiki/index.php?title=Literatura i tam: https : //drive.google.com/file/d/1O6tuJn-4DSybrKUxW5xklYkFyaKdlMtG/view
mono @2020-10-28 12:49:45
Jeszcze linki: 1. Aktualny adres do mnie: mono małpa i-demo.pl 2. Link do archiwum z biblioteką: http : //mono.i-demo.pl/vbxe/vbxefxula.zip