Tajemnice ATARI

Kompresja od środka


    Kompresja danych to tylko połowa sukcesu. Nigdy do końca nie wiadomo, czy kompresja przebiega prawidłowo, dopóki nie powstaną procedury DEKOMPRESJI, czyli odkodowywania. Dziś ostatnia część tych procedur. Tym, którzy potrafili napisać je sami składam gratulacje - naprawdę to nie jest takie łatwe! A więc do roboty.

    Pobierzmy jeden bit. Sprawdźmy, czy podczas procedury CIO nie wystąpił błąd (jeśli znacznik N jest ustawiony, to znaczy, że wystąpił) lub czy plik nie został odczytany w całości.
* rozpakowywanie 
deco_f jsr getbit
     bpl *+5
     jmp deco_er
    Skontrolujmy flagę C procesora. W przypadku ustawienia znaczy to, że następny będzie tylko jeden bajt.
     bcs only_you
    Flaga skasowana świadczy o większej ilości takich samych bajtów. Stała OUTBIT była dokładnie opisywana w poprzednim odcinku, lecz skupienie większej uwagi na jej wartości nie będzie błędem. Wartość stałej znacznie wpływa na stopień kompresji danych, np. dla danych, w których liczba pojedynczych bajtów nie przekracza trzech (np.tekst) stała powinna przyjąć wartość dwa; dla danych, w których liczba bajtów nie przekracza siedmiu znaków (np. fonty, skomplikowane obrazki w grafice 8) stałą należy przyjąć równą trzy. Dla pozostałych zbiorów wartość stałej można wybierać doświadczalnie, pamiętając o tym, że Jej wartość nie jest zapamiętywana przez ATARI. Zmusza to użytkownika do stosowania różnych wersji programu do różnego rodzaju plików. Szybko przekonacie się jednak, że istnieją o wiele wydajniejsze algorytmy kompresji i odstąpicie od PSE. Powróćmy jednak do naszych wskaźników.

    Pobierz wskaźnik o długości OUTBIT bitów i zapamiętaj jego wartość w zmiennej deco_cnt.
     ldx #outbit 
     jsr gt_xbit
     bpl *+5
     jmp deco_er
     sta deco_cnt
    Następnie odbierz znak o długości ośmiu bitów, który się powtarza i przechowaj go.
     jsr gt_8bit
     bpl *+5
     jmp deco_er
     sta rebyte
    Kolejno odczytując wartość znaku, zapisuj go w pamięci.
deco_p lda rebyte
     jsr put_one
    Przed zapisaniem bajtu sprawdźmy, czy jest miejsce na jego wpisanie. Znacznik C skasowany - jest miejsce.
     bcc deco_j
    Znacznik ustawiony - niestety, miejsca brak. Odpowiedni komunikat poinformuje nas o tym niezwykłym fakcie.
deco_t ldx #long_m
     jsr dsp_msg 
     jmp decode
    Zmniejszmy licznik powtórzeń znaku.
deco_j dec deco_cnt
deco_cnt equ *+1
     lda #0 -*
    Porównajmy wartość licznika z liczbą $ff. Nie jest ona przypadkowa, dlaczego nie porównać go z zerem, lub nie sprawdzić czy nie osiągnął wartości ujemnej przez BMI? Ja wiem, a Wy wiecie? Piszcie!
     cmp #$ff
     bne deco_p
     jmp deco_f
    Do wysłania pojedynczy bajt.
only_you equ *
     jsr gt_8bit
     bpl *+5
     jmp deco_er
     jsr put_one
     bcs deco_t - brak miejsca
    Wszystkie dotychczasowe operacje powtarzaj w kółko aż do odczytania wszystkich bajtów pliku lub całkowitego wypełnienia pamięci danymi.
     jmp deco_f
    Na koniec wystarczy sprawdzić, czy przyczyną przerwania procesu odkodowywania był błąd transmisji czy zakończenie pliku.
deco_er cpy #eof
     beg deco_out
     jmp decode
    Został osiągnięty koniec pliku.
deco_out ldx #chn1
     jsr close
    Zapytajmy o specyfikacje zbioru do zapisu, otwórzmy plik i wyślijmy cały plik, uprzednio obliczając jego długość.
     ldx #put_m
     jsr get_text
     bpl *+5
     jmp decode

     ldx #chn1
     lda #8
     jsr open
     bmi deco_out

     sec
     lda pse_z0
     sbc bufa
     sta io_len,x
     lda pse_z0+1
     sbc bufa+1
     sta io_len+1,x

     jsr mcio
     jmp deco_out
    I to już koniec kompresji metodą Powtarzających Się Elementów. Wszyscy, którzy zrozumieli, na czym ona polega mogą przystąpić do czytania dalszej części artykułu. Pozostali niechaj wysilą swe szare robaczki i zaczną od początku.

    Dla wszystkich, którzy lubią wiedzieć, co się dzieje i widzieć, jak się dzieje pozostaje dopisać poniższe procedury, które sprawią, że po naciśnięciu klawisza SHIFT podana zostanie wiadomość o ilości przeglądniętych (spakowanych) danych.

    Aby uzyskać wynik wyrażony procentowo należy skorzystać z wzoru:

    K=(X/Y)*100%

    gdzie:

    X - zmienna określająca liczbę spakowanych danych. W końcowej fazie kodowania zmienna przyjmie wartość Y - cały plik poddany kompresji.

    Y - stała wyrażająca rozmiar całego pliku objętego kompresją.

    Ze względu na bardzo prostą budowę procedur, co wiąże się z bardzo dużym czasem wykonywania, wynik będzie wyświetlany wówczas, gdy użytkownik naciśnie klawisz SHIFT. Dane te aktualizowane będą, jeżeli klawisz nie zostanie zwolniony. Powtórne naciśnięcie spowoduje kolejną aktualizację.
     lda skctl
     and #shift
     bne comp_c - SHIFT zwolniony!
    Wartość zmiennej X obliczymy w bardzo prosty sposób, odejmując od aktualnej pozycji wskaźnika kodowania (PSE_ZO) wartość początku bufora kompresji (BUFA). Starszy bajt wyniku przekażmy w rejestrze X, zaś młodszy w akumulatorze.
* wyświetl wyniki pakowania 
     sec
     lda pse_z0
     sbc bufa
     pha
     lda pse_z0+1
     sbc bufa+1
     tax
     pla
    Wynik wyświetlony zostanie w szóstej pozycji za obecną pozycją kursora na ekranie.
     ldy #6
     jsr prnt_prc
    Powyższe linie należy dołączyć do listingu z poprzedniego odcinka przed etykietą COMP_B EQU *+1 (końcówka bloku kompresji).

    Niezależnie od nas pokażmy uzyskane wyniki pakowania. Zmienna WRITE zawiera liczbę wysłanych pełnych bajtów.
* wyświetl wyniki kompresji
     ldy #6
     lda write
     ldx write+1
     jsr prntprc
    Przenieśmy kursor do następnej linii, aby wyświetlany kolejny tekst nie zniszczył naszej informacji.
      lda #eol
      jsr dschar
    Powyższy fragment należy umieścić przed ostatnią linią w bloku kompresji (przed JMP CODE_OUT).
* wyślij procentowo
* uzyskane wyniki
prnt_prc sta prc_lsb
     stx prc_msb
     lda #0
     sta prc_r
     sta prc_r+1
     sta prc_r+2
* dodaj do siebie 100 razy
* otrzymaną wartość
     ldx #100
pr_pl clc
     lda prc_lsb
     adc prc_r
     sta prc_r
     lda prc_r+1
     adc prc_msb
     sta prc_r+1
     bcc *+5
     inc prc_r+2
     dex
     bne pr_pl
* wynik dodawania podziel
* przez READ stosując
* kolejne odejmowanie
pr_pd sec
     lda prc_r
     sbc read
     sta prc_r
     lda prc_r+1
     sbc read+1
     sta prc_r+1
     lda prc_r+2
     sbc #0
     sta prc_r+2
     bcc pr_pt
     inx
     bne pr_pd
    W rejestrze X otrzymamy wynik obliczeń podany w procentach. Pozostaje jedynie doprowadzić go do czytelnej postaci i pokazać na ekranie.
pr_pt stx byte

     lda #')'-$20
     jsr to_msg
     lda #'%'-$20
     jsr to_msg

     jsr convr
     jsr word
     jsr disp_2
     lda word+1
     jsr disp_1
     lda #'('-$20
     jmp to_msg

disp_2 pha
     jsr disp_1
     pla
     lsr @
     lsr @
     lsr @
     lsr @
disp_1 and #%00001111
     ora #'0'-$20
to_msg sta (savmsg),y
     dey
     rts

convr lda #0
     sta word
     sta word+1
     lda #8
     sed
cv1  asl byte
     lda word
     adc word
     sta word
     rol word+1
     dex
     bne cv1
     cld
     rts
byte    org *+1
word    org *+2
prc_lsb org *+1
prc_msb org *+1
prc_r   org *+3
    Wszystkie powyższe procedury celowo nie są skomplikowane, co łączy się z olbrzymim czasem wykonywania. Jak inaczej można pomnożyć lub podzielić liczbę trzybajtową pozostanie tajemnicą aż do następnego odcinka. Za miesiąc pełny opis nowej, lepszej, wydajniejszej metody zwanej Imploding.

MATHNOID'93


   P.S. Szczególne podziękowania dla M-y P. oraz A.P. za wyrozumiałość podczas powstawania artykułu.



Powrót na start | Powrót do spisu treści | Powrót na stronę główną

Pixel 2002