speccy.pl
Facebook Like


SPECCY.PL

[SPECCY.PL PARTY 2023.1]

[WIKI SPECCY.PL]
Polecamy

KOMITET SPOŁECZNY KRONIKA POLSKIEJ DEMOSCENY
PIXEL HEAVEN 2023
AYGOR
Forum ZX Spectrum
Zawartość panelu chwilowo niedostępna
Archiwum plików ZX Spectrum
Nawigacja
Loadery według Kato, część 2: sztuka skracania.
Loadery według Kato, część 2: sztuka skracania.

Mamy kolejne programy z funny loaderami od Kato. Ciekawsze, bo na 128K, wykorzystujące drugi ekran. Ale dalej mają 300 bajtów, lub mniej. To musi być ważna liczba. Co najmniej tak ważna jak 42 ;)


COPY 128 (v1.3)


Ekran wybucha i nagle zmniejsza się. Całe ładowanie obserwujemy w okienku 26 na 18. Widać border, czarny środek, a dookoła biało. Na "borderze" normalnie są paski, na "ekranie" litera po literze pojawia się napis COPY 128, który potem szybko zmienia kolory. Jest to jakby loader od COPY 80, ale w okienku ;) Wszystko jest ładnie zmniejszone, kształt liter jest proporcjonalny, choć przecież tym razem nie są zrobione na atrybutach. Niepokój, że będzie trzeba używać kopiera w takim małym okienku mija po załadowaniu całości - mamy kolejny "wybuch" i ekran wraca do normalnych wymiarów.
Na Spectrum 48K oczywiście nie ma tego efektu, podczas ładowania miga tylko mały napis COPY 128 na czerwono, a po załadowaniu "duży" napis dalej miga i program jest zablokowany.

Paski na cały ekran na ZX 128K już były, a takie coś jak zrobić?
Aby uzyskać paski na ekranie, należy pokolorować jeden ekran na niebiesko, drugi na żółto i przełączać ekrany synchronicznie z borderem. Żeby tylko na części ekranu były paski, wystarczy nadać te kolory tylko fragmentom ekranu - tam gdzie mają być paski! Reszta ma mieć tą samą treść i te same kolory na obu ekranach, a na border nie wysyłać nic. Oczywiście napis COPY 128 trzeba w tej sytuacji też wyświetlać na obu ekranach.

Procedura efektów w trakcie ładowania jest bardzo krótka, ma tylko 46 bajtów plus 7 na paski na ekranie (które tak naprawdę polegają na odpowiednim przygotowaniu tła). Licznik w DELAY jest zmniejszony z 22 na 14. Czyli luzik.
Co konkretnie robi? Głównie czeka ;)
Przed uruchomieniem ładowania z taśmy B jest ustawiane na 24, czyli przez 24*256=6144 wywołań (384 bajty z taśmy) procedura nie robi nic. Potem jak już zacznie, między literami przerwy są krótsze - najpierw 1024 wywołań (~340 milisekund, zakładając 1500 bitów/sek), a potem 256 wywołań (~80ms). Jak już działa, to w czasie jednego wywołania zmienia tylko jeden bajt atrybutów. A w zasadzie ½, bo musi jeszcze zmienić na drugim ekranie. Ale nie ma się co śpieszyć, zmiana koloru jednej litery następuje w ciągu 12 wywołań, czyli średnio w 4 milisekundy. Gdyby nie było przerw między literami – cały napis zająłby ~0,112 sekundy.

        EXX               ;alternatywny zestaw rejestrów
        LD A, B          ;działanie czy czekanie?
        AND A           ;
        JR Z, L_AAF4  ;działanie
        DEC D            ;\
        JR NZ, L_AB14;--pauza
        DEC B            ;/
        JR L_AB14      ;
L_AAF4: LD A, C      ;w HL adres atrybutu 
        ADD A, (HL)   ;w C przyrost koloru
        AND $07        ;ogranicz kolor do wartości 0-7
        LD (HL), A      ;wpisz zmieniony kolor
        DEC E            ;licznik powtórzeń
        BIT 0, E         ;czy już oba ekrany?
        JR NZ, L_AB14 ;nie, wyjście
        LD A, L	     ;\
        ADD A, $20     ;-linia niżej
        LD L, A	     ;/
        SUB $7F         ;4 linie wyżej, o jeden w prawo, czy już koniec litery?
        JR C, L_AB14  ;nie, wyjście
        LD L, A          ;tak, zmień adres w HL
        BIT 0, L         ;pierwsza czy druga kolumna znaku?
        JR Z, L_AB14  ;pierwsza kolumna, wyjście
        LD B, C          ;druga kolumna, czyli teraz pora na pauzę
        CP $19          ;ale czy nie koniec napisu?
        JR NZ, L_AB14 ;jeszcze nie
        LD L, $08       ;koniec, przejdź na początek napisu
        LD C, $01       ;przyrost koloru - teraz będzie 1
L_AB14: LD A, $0E
        EXX

Opóźnienia w procedurze zmieniającej kolor liter są tak dobrane, że napis jest pokazywany w kolorze zielonym, potem zmienia kolory i kończy się również zielonym, takim jak jest docelowo w kopierze. Końcowy ”wybuch” jakby powiększa ekran ;)
Z ciekawostek: licznik powtórzeń w E kontroluje czy kolor został zmieniony już na obu ekranach, nie jest nigdy ustawiany, zmiana wiersza następuje co drugi raz i także co drugi raz zawartość E jest parzysta. I to właśnie jest sprawdzane. Z kolei w rejestrze C jest przechowywana wartość przerwy między literami (4 lub 1) która jednocześnie jest „przyrostem” koloru – tyle jest dodawane do wartości na ekranie.

Bardzo ładne jest uruchomienie loadera: 1988 READ Kato
Widać tylko tyle! Polecam wszystkim obejrzenie i samodzielne dojście jak to jest zrobione, używając np. Openera. Bardzo fajny pomysł!

W kodzie też ciekawostki. Na początku loader konstruuje procedurę ładującą, kopiując ją z ROM. Zamiast używać LDIR i później zmieniać adresy skoków JP i wywołań CALL jak w poprzednich loaderach, tu jest ciekawa procedura kopiowania i jednoczesnej relokacji:

        LD DE, $053F    ;skąd kopiować
        LD HL, $AA3F    ;dokąd kopiować
        LD B, H             ;ile bajtów: $AA
        LD SP, HL
        EX DE, HL
L_5CE5: LD A, (HL)      ;kopiowany bajt
        CP H                  ;czy zmienić?
        JR NZ, L_5CEA    ;nie, omiń
        LD A, D              ;tak
L_5CEA: LD (DE), A     ;wpisz bajt pod nowy adres
        INC HL
        INC DE
        DJNZ L_5CE

Procedura sprawdza KAŻDY kopiowany bajt i $05 zamienia na $AA. W zasadzie bez sensu, powinna sprawdzać tylko adresy, ale w tym konkretnym przypadku można było tak zrobić, ponieważ w kopiowanym fragmencie kodu "5" występuje TYLKO jako część adresu. Niestety nie jest uniwersalna - jest dostosowana do tego konkretnego przypadku: przerzucania LOAD pod inny adres.
Jest krótsza od stosowanej w poprzednich loaderach o 7 bajtów. Także i w niej widać optymalizację długości - starszy bajt adresu docelowego jest tak dobrany, żeby był równy długości kopiowanego obszaru (zysk: 1 bajt). Adres docelowy kopiowania jest także użyty jako wskaźnik dna stosu (-1 bajt). A po zakończeniu całej procedury kopiowania - adres w HL wskazuje na tekst w procedurze wyświetlenia napisu COPY 128 (-3 bajty).

Która też jest bardzo ciekawa. Napis jest tworzony ze znaków graficznych (kwadracików) o kodach 128 - 143 ($80 - $8F). Kato użył w niej instrukcji, której nigdy dotąd nie widziałem: RRD. Chyba jednak użył jej inaczej niż zamierzyli twórcy Z80 ;) Jest to najbardziej skomplikowana instrukcja przesuwania zawartości rejestrów. Używa A i (HL), dokonując operacji na połówkach bajtów.
Tu jest użyta w celu "rozkompresowania" napisu, w którym dwa wyświetlane znaki zajmują jeden bajt w pamięci.

        LD BC, $0818       ;początkowa pozycja wydruku
L_5D04: PUSH HL         ;HL wskazuje napis
        CALL $0DD9         ;ustal pozycję wydruku	
        LD D, $07            ;siedem bajtów na wiersz wydruku
        POP HL
L_5D0B: LD A, $88       ;8 do "lewej" i 8 do "prawej" połówki A 
        RRD                    ; "prawa" połowa (HL) trafia do "prawej" połowy A czyli x=(HL) AND 00001111, y=(HL) AND 11110000
        RST $10             ;A=$8x - wyświetl 
        LD A, (HL)          ;"prawe" 8 z A trafiło do "lewej" połowy (HL), załaduj
        RST $10             ;A=$8y - wyświetl
        INC HL               ;następny bajt tekstu
        DEC D                ;koniec wiersza?
        JR NZ, L_5D0B	
        DEC B
        LD A, B
        CP $05
        JR NZ, L_5D04

Dzięki użyciu RRD bajt jest rozdzielany na dwie połówki, bez konieczności używania masek AND 00001111 i AND 11110000, które wydłużyłyby procedurę o 4 bajty. Finalnie jest dłuższa tylko o 5 bajtów niż taka sama bez dekompresji, a dane upchnięte są w dwa razy mniejszym obszarze: zajmują 21 bajtów zamiast 42 (+5-21=-16 bajtów).
Procedura ma 27 bajtów, a więc tyle samo, co w sumie prostsza, procedura napisu w omawianym dalej Compress COPY 128! Dane zajmują tylko 1 bajt więcej.
Nie jest uniwersalna. Można ją użyć do wyświetlania w zasadzie tylko tych kwadracików, UDG lub innych znaków z zestawu 16 sztuk. Jej drugą wadą jest to, że jest jednorazowa - po wykonaniu zawartość pamięci jest zmieniona.

Po zakończeniu powyższej procedury w rejestrze A jest 5. Wartość zostaje użyta, jako numer włączanego banku. (-2 bajty). Jak jeszcze zaoszczędzić kilka bajtów? Na przykład tak:

        LD HL, 16384
        LD DE, 49152
        LD BC, 6966
        LDIR

To kopiowanie ekranu 1 do ekranu 2. Ale długość się nie zgadza, zamiast 6912 jest 6966. To po to, żeby po zakończeniu w rejestrze E była wartość 54. Zostanie użyta jako kolor obszaru ekranu (-2 bajty).

Zabawny szczegół jest też w procedurze "wybuchu":

        ...
        HALT
        HALT
        DEC C
        INC C	            ;DEC C a potem INC C nie robi nic!
        OUT ($FE), A
        RET
        CALL PO, $BBC0
        ...

Przed OUT ($FE),A mamy dwie instrukcje które razem nic nie robią! To prawda, ale tak musi być, ponieważ w tym przypadku DEC C to 13 - kod ENTER w BASIC, czyli koniec linii! Po niej jest następna linia. Tak. Dwa następne bajty to tak naprawdę numer linii, kolejne dwa bajty - długość linii. Tajemnicze CALL PO,... to treść linii. Numer i długość nie mają w tym przypadku znaczenia, więc zostały wykorzystane do wpisania fragmentu programu! (-3 bajty).
I w końcu ładowanie bloku

        SCF
        SBC A, A
        CALL $AA56

zamiast tradycyjnego, pisanego przez wszystkich odruchowo: SCF, LD A, $FF jest SCF, SBC A,A (-1 bajt).

Copy 128 to kopier na ZX Spectrum 128K. Pojemność 125749 bajtów, czyli można załadować na raz kilka gier z 48K. Wygląd podobny jak COPY 80, identyczna obsługa, ale nie ma turbo.


ZEBRA COPY


Bardzo sympatyczny pomysł - wskaźnik postępu. Na początku jest ładny, dość krótki CLS typu "zamykanie bramy od garażu", a później w niebieskiej ramce pojawia się mini-ekranik z paskami ładowania, a obok wskaźnik postępu.

CLS:
        LD HL, 22528
        LD D, 12
L_5D25: LD B, 64
L_5D27: LD (HL), C
        INC HL
        DJNZ L_5D27
        HALT
        DEC D
        JR NZ, L_5D2

Ten program akurat nie jest na Spectrum 128K.
Procedura efektów w trakcie ładowania ma tu 68 bajtów. Krótka. Większa część programu zajmuje się chyba narysowaniem ramki ;) Licznik w DELAY jest zmniejszony z 22 na 12, 9 lub 7 w zależności która część programu jest wykonywana. Procedura nie jest uniwersalna, nie da się jej zastosować do bloków innej długości. Zastanawiając się jak zrobić taki efekt, myślałem że rysuje kreskę w miejscu obliczonym na podstawie pozostałej do załadowania ilości bajtów (w DE), dzieląc przez współczynnik skalujący, ale oczywiście tu jest prościej – w KAŻDYM wywołaniu rysuje pionową kreskę, a co 256 wywołań (czyli co 16 załadowanych z taśmy bajtów) zmienia tylko jej pozycję na następną. Tak naprawdę rysuje w co drugim wywołaniu, w pozostałych przygotowuje dane.

Uruchomienie: LOAD ""CODE USR 126p
Haha, już był podobny patent z literami w poprzednich loaderach, tyle że nie w LOAD. Ale już wiemy, że LOAD nie wykonuje się ;)
Czy Kato też miał "malucha"? ;)

Zebra Copy to pierwszy program kopiujący taśma-dysk na FDD3000. Obsługuje turbo, ma kilka trybów pracy, wersja 1.3 sprawdza ilość wolnego miejsca na dysku przed zapisem. Umożliwia automatyczną konwersję komend ładowania w kopiowanych programach na dyskowe. Jest dołączona instrukcja.
Jest tu też nowy pomysł na nagłówek. Po wszystkich tych nagłówkach z BRIGHT, FLASH i innych kolorach liter, tu mamy nazwę w dwu wierszach – ”COPY” jest dokładnie pod ”ZEBRA”.


Compress COPY 128 (v2.7)


Ten loader robi największe wrażenie. Fajnie się zaczyna, bo już w czasie pilota - niebiesko-czerwone paski z bordera wjeżdżają(!) od dołu na ekran. Jakby kurtyna zamykająca się w pionie. Po rozpoczęciu ładowania jest chwila ciszy, po czym kurtyna otwiera się - tym razem w poziomie - odsłaniając czarne tło i zielony napis COPY 128. Wygląda to tak, jakby były trzy ekrany: dwa użyte na paski a na trzecim napis na czarnym tle ;)

Świetny efekt, na pełen ekran i w pełnej rozdzielczości - "kurtyna" rozsuwa się co 1 pixel.
Po załadowaniu na Spectrum 48K, nie ma oczywiście efektu pasków na ekranie, rozsuwająca się kurtyna jest, ale jednobarwna. Po zakończeniu ładowania miga napis i blokada.

Uważny czytelnik już wie, jak zrobić paski na borderze i równocześnie na nie-całym-ekranie. Tu obszar pasków zmniejsza się w trakcie ładowania, więc trzeba zwiększać obszar, na którym kolory obu ekranów są takie same. Ale jak zrobić napis? Otóż ekrany 0 i 1 mają kolory jak paski na borderze: 0-czerwone tło, 1-niebieskie tło. Kolor atramentu (INK) na obu ekranach jest taki sam od samego początku - w większości czarny, na dole zielony układający się w napis COPY 128. Nie widać tego, bo nic nie jest narysowane. Tu jest sekret: „czarne tło” też jest zrobione atramentem!
W trakcie ładowania rysowane są, zaczynając od środka, pionowe kreski przez cały ekran na obu ekranach. Na obu ekranach w tych miejscach jest to samo więc przełączanie ekranów nie powoduje pasków. Po kolei wszystkie pixele są "zapalane". Na końcu cały ekran jest "zapalony", widać atrament, nie widać tła (a więc pasków). Niby proste, ale jak się już zobaczy.

Efekt niemożliwości - trzech kolorów w obrębie jedego pola 8x8 (czerwony, niebieski i zielony) - występuje dzięki dwóm ekranom stodwudziestkiósemki. W pewnych momentach odsłaniania "kurtyny" wydaje się że są 4 kolory (plus czarny), ale to dlatego, że myli nas krawędź kurtyny. W kwadracie 4 pixele w lewo od krawędzi i 4 w prawo rzeczywiście są czasem CZTERY kolory, ale taki kwadrat jest przesunięty względem pola atrybutów.

A jak zrobić wjeżdżające na ekran paski w czasie pilota? Oczywiście używając tej samej procedury ładującej, przełączającej ekrany synchronicznie z borderem. Na starcie oba ekrany mają kolor niebieski, a potem na drugim ekranie trzeba zmienić kolor tła jednego wiersza na czerwony i odczekać odpowiednią chwilę zanim przystąpi się do kolejnego wiersza.

Paski czerwono-niebieskie to efekt zmiany AND 7 na AND 3 w procedurze EDGE-1 mierzącej długość impulsów z taśmy. To oczywiście nie jedyna zmiana w EDGE-1, została też rozszerzona o przełączanie banków:

;EDGE-1 klasyczny
;...
LD A,C
CPL
LD C,A
    
    
    
    
    
AND 7
OR 8
    
    
    
OUT (254),A


SCF
RET
;Copy 128
;...
LD A, C
CPL
LD C, A
AND 15
OR 15
        
        
        
          
     
PUSH BC
LD BC,32765
OUT (C), A
        
POP BC
       
SCF
RET
;Compress 128
;...
LD A,C
CPL
LD C,A
AND 8
OR 23
PUSH DE
LD E, A
LD A, C
AND 3
OR 8
PUSH BC
LD BC,32765
OUT (C), E
OUT (254), A
POP BC
POP DE
SCF
RET

Zwraca uwagę dbałość o wygląd, pomimo presji na skracanie – obie instrukcje OUT są jedna po drugiej, choć tak zajmuje to więcej miejsca (+3 bajty). Gwarantuje to najlepszą synchroniczność pasków na ekranie i na borderze. W innych tego typu realizacjach, gdzie są inne instrukcje między obiema OUT, efekt jest gorszy. Z porównania procedur EDGE-1 w obu programach wynika jeszcze, że inaczej rozwiązany jest w nich dostęp do obu ekranów. W Copy 128 oba ekrany są adresowane pod 49152 i EDGE oprócz przełączania który ekran jest wyświetlany, przełącza również który jest dostępny pod adresem 49152.
Na port 32765 wysyłane są:
00010111 – rom 1, screen 0, bank 7 (pod 49152 screen 1)
00011101 – rom 1, screen 1, bank 5 (pod 49152 screen 0).
W Compress Copy 128 – przełączany jest tylko ekran wyświetlany. Ekran 0 jest dostępny standardowo pod 16384, a ekran 1 pod 49152.
Na port 32765 wysyłane są:
00010111 – rom 1, screen 0, bank 7 (pod 49152 screen 1)
00011111 – rom 1, screen 1, bank 7 (pod 49152 screen 1).
W konsekwencji w trakcie ładowania w Copy 128 bajty na ekran są ładowane dwa razy w to samo miejsce, a w Compress Copy 128 jest użyta procedurka:

$AB15:  LD (HL), A
        SET 7, H
        LD (HL), A
        RES 7, H
        RET

I tak jest szybciej, bo są ładowane oba ekrany za jednym wywołaniem EDGE-1.

W tym loaderze napis COPY 128 tworzony jest z atrybutów, jeden bajt koloru ekranu jest zapisany na 1 bicie wzorca (zapalony czy nie), więc 1 linia jest zapisana w 4 bajtach:

        LD HL, $5A40    ;adres na ekranie
        LD B, $14         ;długość wzorca
L_5D2B: LD C, E
        LD D, (IX-$1D)
        INC IX
L_5D31: SLA D
        SBC A, A
        AND $04           ;kolor zielony
        OR (HL)
        CALL $AB15      ;wpisz na obu ekranach
        INC HL
        SLA C
        JR NC, L_5D31
        DJNZ L_5D2

Sama procedura ma 27 bajtów + 20 bajtów danych. Jest uniwersalna, można użyć z inną długością i/lub w innym miejscu na ekranie. Co niezwykłe - nigdzie nie ma ładowania wartości początkowej do IX (-4 bajty). Skąd się zatem bierze? Wskazuje na następny bajt po loaderze, bo zostaje po wyjściu z procedury ładowania loadera.

Procedura efektów w trakcie ładowania ma tu 103 bajty. Licznik w DELAY jest zmniejszony z 22 na 18 lub 3 (24 lub 0 dla pilota), a stała w pętli LD-8-BITS jest zmniejszona z $CB na $C8.

Uruchomienie: PAPER PI*USR Kato.
Od "pi razy drzwi"? Papier między jeden a dwa, czyli mniej więcej 1 i 1/2 :) "Kato" w tym przypadku jest zmienną. Może ktoś się nabierze i rzeczywiście pomnoży adres przez 3,14... ;)

To jest następna wersja kopiera na 128. Dodana kompresja w locie. Pojemność "statyczna" 125696, mniejsza o 53 bajty niż COPY 128 v1.3, ale kompresja umożliwia załadowanie więcej. Oczywiście gdy kopiowane gry/programy same nie są już skompresowane.

Programy Kato w archiwum speccy.pl

Autor: Phonex

Licencja Creative Commons Artykuł autorstwa Phonexa został wydany na licencji Creative Commons Uznanie autorstwa - Użycie niekomercyjne - Bez utworów zależnych 4.0 Międzynarodowe License.
W oparciu o utwór dostępny pod adresem http://speccy.pl/articles.php?article_id=47

5
PopoCop dnia 12.09.2015 11:25:49

Skoro programy są krótkie, a efekty ładowania ciekawe to może wstawić animowane ilustracje?

357
Konto ukryte dnia 28.09.2015 13:45:56

Jak będą ruchome obrazki, to kto będzie czytał tekst? Z przymrużeniem oka I już na pewno nikt nie ściągnie...

5
PopoCop dnia 09.10.2015 10:06:27

Myślałem, że to artykuł dla ambitnych, chcących wiedzieć "co w kodzie piszczy", a nie oglądaczy animacji/obrazków Uśmiech

Dodaj komentarz
Zaloguj się, aby móc dodać komentarz.
Oceny
Tylko zarejestrowani użytkownicy mogą oceniać zawartość strony
Zaloguj się , żeby móc zagłosować.

Brak ocen. Może czas dodać swoją?