VBXEFXULA by Mono 2016-07-13 23:01:22

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:

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:

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"

czyli:

- $00..$07, $08, $10, $18, $20, $28, $30, $38 oraz
- $40..$47, $48, $50, $58, $60, $68, $70, $78.

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:

$00: PF0
$01: PF1 - %00111000
$02: PF2 - %00000000
$03: PAL

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:


bcb_flashadvance:
FX_FLASHCNTR:
?srcad .long 0
?srcdy .word 0
?srcdx .byte 0
?dstad .long FX_FLASHCNTR
?dstdy .word 0
?dstdx .byte 0
?width .word 1-1
?height .byte 1-1
?and .byte %00000000
?xor .byte 8
?mode .byte ADDMODE


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).

Zadanie określania znacznika zamiany kolorów atrybutu FX_FLASHFLAG realizuje następujący blok BCB:


bcb_flashflag:
?src .long FX_FLASHCNTR
?srcdy .word 0
?srcdx .byte 0
?dst .long FX_FLASHFLAG
?dstdy .word 0
?dstdx .byte 0
?width .word 1-1
?height .byte 1-1
?and .byte %10000000
?xor .byte %00000000
?mode .byte COPYMODE


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:


bcb_copyink:
?src .long FX_ULA_ATR
?srcdy .word 32
?srcdx .byte 1
?dst .long FX_ATR+PF1 ;COLPF1
?dstdy .word 4*32
?dstdx .byte 4*1
?width .word 32-1
?height .byte 24-1
?and .byte %01000111
?xor .byte %00000000
?mode .byte COPYMODE

bcb_copypaper:
?src .long FX_ULA_ATR
?srcdy .word 32
?srcdx .byte 1
?dst .long FX_ATR+PF2 ;COLPF2
?dstdy .word 4*32
?dstdx .byte 4*1
?width .word 32-1
?height .byte 24-1
?and .byte %01111000
?xor .byte %00000000
?mode .byte COPYMODE


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:


bcb_maskinkfill:
?src .long 0
?srcdy .word 0
?srcdx .byte 0
?dst .long FX_ATR+PF1 ;COLPF1
?dstdy .word 4*32
?dstdx .byte 4*1
?width .word 32-1
?height .byte 24-1
?and .byte %00000000
?xor .byte %10111111
?mode .byte COPYMODE


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.


bcb_maskinkmake:
?src .long FX_ULA_ATR
?srcdy .word 32
?srcdx .byte 1
?dst .long FX_ATR+PF1 ;COLPF1
?dstdy .word 4*32
?dstdx .byte 4*1
?width .word 32-1
?height .byte 24-1
FX_FLASHFLAG:
?and .byte %10000000
?xor .byte %00000000
?mode .byte OVERMODE


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:


bcb_maskinkinvert:
?src .long 0
?srcdy .word 0
?srcdx .byte 0
?dst .long FX_ATR+PF1 ;COLPF1
?dstdy .word 4*32
?dstdx .byte 4*1
?width .word 32-1
?height .byte 24-1
?and .byte %00000000
?xor .byte %11111000
?mode .byte XORMODE


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:


bcb_maskpapermake:
?src .long FX_ATR+PF1 ;COLPF1
?srcdy .word 4*32
?srcdx .byte 4*1
?dst .long FX_ATR+PF2 ;COLPF2
?dstdy .word 4*32
?dstdx .byte 4*1
?width .word 32-1
?height .byte 24-1
?and .byte %11111111
?xor .byte %00111111
?mode .byte COPYMODE


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.


bcb_attrink:
?src .long FX_ULA_ATR
?srcdy .word 32
?srcdx .byte 1
?dst .long FX_ATR+PF1 ;COLPF1
?dstdy .word 4*32
?dstdx .byte 4*1
?width .word 32-1
?height .byte 24-1
?and .byte %11111111
?xor .byte %00000000
?mode .byte ANDMODE

bcb_attrpaper:
?src .long FX_ULA_ATR
?srcdy .word 32
?srcdx .byte 1
?dst .long FX_ATR+PF2 ;COLPF2
?dstdy .word 4*32
?dstdx .byte 4*1
?width .word 32-1
?height .byte 24-1
?and .byte %11111111
?xor .byte %00000000
?mode .byte ANDMODE


Gotowe!

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:


bcb_maskinkmake:
?src .long FX_ULA_ATR
?srcdy .word 32
?srcdx .byte 1
?dst .long FX_ATR+PF1 ;COLPF1
?dstdy .word 4*32
?dstdx .byte 4*1
?width .word 32-1
?height .byte 24-1
FX_FLASHFLAG:
?and .byte %10000000
?xor .byte %00000000
?mode .byte OVERMODE


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)!

Gdzie zatem znajduje się FX_FLASHCNTR?


bcb_flashadvance:
FX_FLASHCNTR:
?srcad .long 0
?srcdy .word 0
?srcdx .byte 0
?dstad .long FX_FLASHCNTR
?dstdy .word 0
?dstdx .byte 0
?width .word 1-1
?height .byte 1-1
?and .byte %00000000
?xor .byte 8
?mode .byte ADDMODE


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:
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:


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:


EMP8 = $70
LMS = $40
GFXF = $f
JVB = $41

dlist:
:3 .byte EMP8
.rept 192
.byte LMS|GFXF
.word A8_SCR+(((# & %111) << 8)
| ((# & %111000) << 2)
| (# & %11000000) << 5)
.endr
.byte JVB
.word dlist


A8_SCR to stała zdefiniowana w pliku vbxefxula.icl i określa adres pamięci ekranu w RAM-ie Atari zgodnie z jej położeniem w ZX Spectrum 48k.

5. Włączyć wąski ekran i display list:


DMACTS = $22f
DLPTRS = $230

lda #%00100001
ldx #< dlist
ldy #> dlist
sta DMACTS
stx DLPTRS
sty DLPTRS+1



6. Zdefiniować handler VBLK uruchamiający blitter VBXE:


JEXITVB = $e462

vblkint:
jsr ula_refresh
jmp JEXITVB


7. Zainstalować handler VBLK:


VBLKD = 7
JSETVBV = $e45c

ldy #< vblkint
ldx #> vblkint
lda #VBLKD
jsr JSETVBV


8. Modyfikować atrybuty ULA w pamięci:


lda #%10111000
sta A8_ATR


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:


; blitter interrupt routine

irqint:
bit vbxefx
bpl ?skip1
pha
tya
pha
ldy #IRQ_STATUS
lda (vbxead),y
beq ?skip2

; reset blitter IRQ

sta (vbxead),y

; 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@atari.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.


Literatura:

1. Tomasz Piórek: "Podręcznik programisty VBXE" - spiflash.org/block/15.html
2. Krzysztof Kuryłowicz, Dariusz Madej, Krzysztof Marasek - "Przewodnik po ZX Spectrum": www.worldofspectrum.org/infoseekid.cgi?id=2000592
_rocky 2016-07-14 00:52:26

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ż).

KMK 2016-07-15 00:01:40

Możliwości ma Spectruma ... ;)

Zniecierpliwiony 2016-07-15 13:06:59

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 :)

fan mono 2016-07-15 16:03:45

mono ho na flaszkę :D

pin 2016-07-15 18:38:36

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

pin 2016-07-20 23:09:48

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!!;)